호댕의 iOS 개발

UISearchController, 검색을 이렇게 간단하게?! 본문

Software Engineering/iOS

UISearchController, 검색을 이렇게 간단하게?!

호르댕댕댕 2022. 2. 25. 19:11

 

연락처 앱이나 리스트가 존재하는 다양한 앱을 사용하다 보면 검색을 통해 해당 검색 요건을 충족하는 것들만 보여주는 기능을 자주 접하게 된다. 

 

바로 아래 그림처럼 말이다. 

이런 것들은 어떻게 구현하는 것일까??? 

 

이미 애플은 이런 검색 기능을 구현할 수 있도록 Class를 만들어놓았다. 

 

그 이름은 바로 UISearchController !! 

이름이 상당히 직관적이다. 

 

그렇다면 UISearchController는 어떻게 사용할 수 있을까?

 

 

1️⃣ UISeachController 초기화하기

일단 DateFormatter 같은 것들을 사용할 때처럼 Initialize를 해줘야 한다. 

private let searchController = UISearchController()

 

초기화를 해줄 때 Initializer는 다음과 같다. 

init(searchResultsController: UIViewController?)

여기서 파라미터인 searchResultsController를 nil로 해주게 되면 검색할 수 있는 컨텐츠를 표시하는 ViewController와 동일한 ViewController에 검색 결과를 표시하게 된다. 

 

즉, 쉽게 말해서 현재 컨텐츠가 표시되는 곳에 검색 결과를 필터링하고 싶다면 nil을 사용해주면 된다. 

다만 tvOS의 경우 nil을 허용하지 않기 때문에 따로 결과를 보여줄 Controller를 제공해야 한다. 

 

 

2️⃣ UISeachController 위치 잡아주기

그리고 UISearchController의 위치를 잡아줘야 한다. 

이때 이미 navigationItem에 searchController 프로퍼티가 존재한다. 

navigationItem.searchController = searchController

따라서 단순히 navigationItem의 searchController에 생성한 UISearchController를 넣어주기만 하면 된다.

그러면 이런 식으로 위치를 잡게 되며 취소X 버튼이 자동으로 생기게 되며 위치도 알아서 자리잡게 된다. 

(참고로 검색으로 나오는 PlaceHolder Text의 경우 지역화를 지원해주면 알아서 search로 바뀐다)

 

 

3️⃣ 검색 필터링 기능 구현 

이제 마지막으로 검색 창에 검색을 하게 되면 리스트가 변할 수 있도록 구현을 해줘야 한다.

바로 위에 있는 GIF처럼 말이다. 

 

이를 위해 UISearchResultsUpdating 프로토콜이 존재한다. 

해당 프로토콜에는 사용자가 Search Bar에 입력하는 정보를 바탕으로 결과를 업데이트할 수 있는 메서드가 존재한다. 

 

그 메서드는 바로 updateSearchResults(for:)이다. 

이는 UISearchResultsUpdating 프로토콜을 채택한 경우 반드시 구현해줘야 하는 필수 메서드이다. 

 

시스템에선 Search Bar가 first responder가 되거나, 텍스트가 변하게 되면 해당 메서드를 호출하게 된다. 

이 메서드 내에서 필요한 필터링 및 검색 결과를 업데이트하는 작업들이 이뤄지게 된다. 

extension MemoListViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard let text = searchController.searchBar.text?.lowercased() else { return }
        filteredMemos = memos.filter {
            guard let title = $0.title?.lowercased() else {
                return false
            }
            return title.contains(text)
        }
        tableView.reloadData()
    }
}

글자가 대문자로 적혀있든, 소문자로 적혀있든 구분 없이 검색을 제공해기 위해 검색 결과를 먼저 소문자로 바꾼 후 메모에 있는 title과 비교를 하는 방식을 선택했다. 

만약 검색한 텍스트를 포함한 메모 제목이 있을 경우 filter될 수 있도록 했고 이는 이미 생성해놓았던 filteredMemos 배열에 들어갈 수 있도록 구현했다. 

 

이후 tableView를 다시 리로드할 수 있도록 해주었다. 

 

 

또한 현재 컨텐츠가 필터링되고 있는지를 판단하기 위해 아래와 같은 연산 프로퍼티를 만들었다. 

    private var isFiltering: Bool {
        let searchController = self.navigationItem.searchController
        let isActive = searchController?.isActive ?? false
        let isSearchBarHasText = searchController?.searchBar.text?.isEmpty == false
        
        return isActive && isSearchBarHasText
    }

연산 프로퍼티에서 확인해주는 조건을 좀 더 자세히 알아보자. 

 

isActive

일단 isActive는 검색 인터페이스의 표시되고 있는 상태를 의미한다. 즉, 검색 결과가 표시되어 있는지를 확인하기 위해 사용하는 프로퍼티이다. 

해당 프로퍼티의 경우 기본값은 false이며, 이렇게 할 경우 Search Bar의 search field를 사용자가 탭을 할 경우 자동으로 search results controller가 보이게 된다. 이때는 isActive 프로퍼티 값이 true로 바뀌게 된다.  

 

하지만 이 프로퍼티가 true가 되면 탭을 하지 않더라도 강제로 검색이 활성화되어 있게 된다. 

그리고 searchBar의 텍스트가 존재하는지를 확인하게 된다. 

 

즉, 위 연산 프로퍼티에선 Search Bar가 활성화되어 있고 텍스트가 있을 경우는 filter가 되고 있다는 것을 알려주는 것이다.

 

 

 

이후 필터링된 결과를 isFiltering 여부에 따라 화면에 보여주는 메모의 종류가 달라지도록 구현을 했다. 


 

처음으로 UISearchController를 사용했는데 SearchBar를 누를 때 자동으로 애니메이션도 적용이 되고 SearchBar에 들어오는 Text도 즉각즉각 알 수 있는 메서드가 존재하여 생각보다 쉽게 검색 기능을 구현할 수 있었다!! 

Comments