검색 기능은 대부분 서비스에 필수적으로 있는 기능이다.
다양한 스타일로 커스텀이 많이 되어있고 내가 참여하고 있는 왁타버스 뮤직팀의 검색 기능 역시
커스텀하여 사용했다.
처음부터 커스텀을 통해 개발하여 기본적인 기능으로는 만들어볼 기회가 없었다.
이번 시간은 애플이 자체적으로 만들어 놓은 UISearchController 기능을 통해 같은 기능을 구현해보자.
실제 서비스
1. 왁타버스 뮤직
세 화면은 앞서 설명한 왁타버스 뮤직앱의 검색화면이다.
- 검색 전 - 추천 컨텐츠
- 검색 중 - 최근 검색어
- 검색 후 - 검색 결과
한번 애플 앱도 살펴보자.
2. 앱 스토어
왁타버스 뮤직앱과 정확히 일치하는 3단 구성이다.
즉, 애플에서도 이 구성을 구현할 수 있게 만들어놨다는 것
이제는 간단하게 만들어보자.
구현
내용을 간단히 살펴보 Search Bar와 함께 검색 결과를 인터렉션을 통해 자연스럽게 보여주도록 관리해주는
뷰 컨트롤러라고 한다.
구조
Root 생성 및 등록
생성 할 때 보면 searchResultController를 옵셔널로 주입받는다.
너무나 간단하게 추측할 수 있다. 검색 결과를 보여주는 화면인 것 같다.
옵셔널인 것을 보니 안 넣어줘도 되지만 우리의 구성요소에는 필요해보이니 넣는 방향으로 해보자.
우리는 검색 결과 화면을 ResultVC라고 하자.
final class RootViewController: UIViewController {
...
private var resultVC: SearchResultViewController = SearchResultViewController()
private lazy var searchController: UISearchController = {
let controller = UISearchController(searchResultsController: resultVC) // 검색 결과 화면 등록과 함께 생성
controller.searchBar.placeholder = "상태,담당자,레이블..." // 플레이스 홀더
controller.searchResultsUpdater = resultVC // 검색 결과에 대한 업데이터 등록
controller.delegate = self // 서치 컨트롤러 델리게이트 등록
controller.searchBar.delegate = self // 검색바 델리게이트 등록
return controller
}()
override func viewDidLoad() {
// 네비게이션 아이템에 등록 이렇게해야 자연스러운 인터렉션이 자동으로 동작한다.
navigationItem.searchController = searchController
}
}
extension RootViewController : UISearchControllerDelegate , UISearchBarDelegate {
// UISearchControllerDelegate 컨트롤러까지 포함한 전체적인 델리게이트
func willPresentSearchController(_ searchController: UISearchController) {
// 검색창을 클릭했을 때 호출됨
searchController.showsSearchResultsController = true // 클릭 되자마자 호출되게 강제
print("clicked")
}
// UISearchBarDelegate 오직 서치bar 관련
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
//검색 취소 클릭 시 호출
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// 검색 버튼 클릭 시 호출
// result뷰컨을 해당 이미지들로 업데이트
resultVC.updateTableView(with: ["house","sun.min","mic","mic.fill","trash"], section: .result)
}
}
결과 화면
여기서 뭔가 빠진 느낌이든다..
바로 3단 구성인데 결과화면은 하나만 쓸 수 있다.
그 뜻은 결과화면으로 화면 2개를 그려야한다는 뜻인데 여기서 나는 검색중에 보여주는 화면과 검색 후 결과화면을
각 시점에 맞는 트리거를 통해 바꿔끼는 작업을 진행해보려한다.
final class SearchResultViewController: BaseViewController<SearchResultViewModel> {
private typealias DataSource = UITableViewDiffableDataSource<Section, CellData>
private typealias Snapshot = NSDiffableDataSourceSnapshot<Section, CellData>
let recommendTexts: [String] = ["Zedd", "Alan Walker", "David Guetta", "Avicii", "Marshmello", "Steve Aoki", "R3HAB", "Armin van Buuren", "Skrillex", "Illenium", "The Chainsmokers", "Don Diablo", "Afrojack", "Tiesto", "KSHMR", "DJ Snake", "Kygo", "Galantis", "Major Lazer", "Vicetone" ]
var filteredArr: [String] = []
private var dataSource: DataSource?
private var tableView: UITableView = {
let tableview = UITableView()
tableview.backgroundColor = .white
return tableview
}()
...
public func updateTableView(with datas: [String], section: Section) {
var snapshot = Snapshot()
snapshot.appendSections([section])
switch section {
case .recommend: // 추천 검색어
snapshot.appendItems( datas.map{CellData.recommand(model: $0)} , toSection: section)
case .result: // 검색 결과
snapshot.appendItems( datas.map{CellData.result(model: $0)} , toSection: section)
}
dataSource?.apply(snapshot, animatingDifferences: false)
}
}
extension SearchResultViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
guard let text = searchController.searchBar.text else { return }
self.filteredArr = recommendTexts.filter{ $0.localizedStandardContains(text) }
if searchController.isActive { // 검색중일 경우 추천검색어 섹션에 필터링된 검색어 넣기
updateTableView(with: filteredArr, section: .recommend)
}
}
}
updateTableVIew 함수를 통해 추천 검색어를 보여줄 지 검색 결과를 보여줄지 결정한다.
검색중은 UISearchResultUpdatin의 updateSearchResults 함수를 이용하고
검색 결과는 위에서 설명한 UISearchBarDelegate의 searchBarSearchButtonClicked를 이용한다.
다음 결과화면 영상을 보자.
검색 전 - 파란 임시뷰
검색 중 - 필터링된 추천 검색어
검색 후 - 이미지 리스트
한가지 SearchResultVC로 2개의 화면 [검색 중, 검색 후]을 모두 가지게 만들 수 있다.
UISearchBarToken
마지막으로 이번 미션에서 새롭게 알게된 개념이 서치바 토큰을 적용해보자.
아이콘과 함께 검색어를 묶어주는 토큰이다.
다음과 같이 searchBar -> searchTextField에 토큰고나련 다양한 프로퍼티와 메서드가 존재한다.
다음은 간단하게 버튼을 눌러 토큰을 추가해보는 것을 살펴보자.
button.addAction { [weak self] in
guard let self else { return }
self.searchController.searchBar.searchTextField.insertToken(UISearchToken(icon: UIImage(systemName: "person"), text: "사람"), at: .zero)
}
출처
'iOS > UIKit' 카테고리의 다른 글
UIHostingController (0) | 2024.10.12 |
---|---|
UIEditMenuInteraction (3) | 2024.10.12 |
NSKeyedArchiver (0) | 2024.09.11 |
NSCoding , NSSecureCoding (0) | 2024.09.10 |
NSObject (1) | 2024.09.10 |