2016.07.03
引言
拖延了大半年最終還是決定老老實實走上寫blog的不歸路,正好最近在用swift重構項目中的搜索頁面,使用了UISearchController(iOS 8.0 & later),在使用過程中發現一些需要注意的地方,且感覺現在網上對UISearchController使用的介紹普遍不清晰或者過于簡單,遂決定拿它開刀,作為人生第一篇blog的主角。
本文盡量從工程實際使用價值的角度來介紹UISearchController,且提供一份示例項目代碼,歡迎留言討論。
概述
首先需要說明的是,UISearchController的使用場景有一定限制,概括來說,目前只有以下兩個場景下在工程使用中的可實現性得到了驗證,其他使用場景基本都存在專場動畫等方面的bug,如果你知道還有其他的實現場景,歡迎留言。
1.在UITableView的tableHeaderView中使用,實現類似微信首頁搜索的場景
2.在NavigationBar的titleView上使用,實現類似淘寶首頁搜索的場景,這種場景可以做適當的衍生,實現navigationBarItem來觸發搜索界面,但歸根結底還是基于此種場景,故不再拿出單獨討論
基本設置
個人感覺UISearchController在工程中最大的意義應該在于可以輕松完成MVC解耦,將一個相對復雜的搜索Scene解耦成一個主MVC+一個子MVC。子MVC可以專注實現進入搜索狀態時響應邏輯和頁面展示等工作,而只需要在創建UISearchController時將子MVC的Controller設置為searchResultsController即可完成關聯。而對于簡單的搜索場景,如果只想在同一個MVC中完成搜索功能,只需將searchResultsController參數傳入nil,并將UISearchController.searchResultsUpdater代理設置為當前MVC。
注意:
使用UISearchController時當前UIViewController有一個很重要的屬性:definesPresentationContext,對應用場景一,應設置為false,防止出現細微的動畫異常(真的很細微,不仔細看看不出來);對場景二,則必須設置為true,但是在當前頁面willDisappear時,應將其設置回默認的false狀態,否則可能對其他頁面產生異常
以下基本設置按實際需求調整:
searchController = UISearchController(searchResultsController: searchResultsVC)
searchController.searchBar.frame = CGRectMake(0, 0, view.bounds.width, 44)
searchController.hidesNavigationBarDuringPresentation = true
searchController.dimsBackgroundDuringPresentation = true
searchController.searchResultsUpdater = searchResultsVC
searchController.delegate = self
searchController.searchBar.delegate = self
在這里介紹兩個坑
一號坑:
searchBar.tintColor會同時改變光標的顏色,所以,如果當你在期望改變按鈕顏色為白色時,光標就看不到了
解決方案:
單獨設置searchBar的按鈕顏色,這里因為當年swift沒有支持可變參數函數,所以在iOS8時代沒有對應的方法,需要用OC封裝一下然后swift調用OC
if #available(iOS 9.0, *) {
//此方法僅對9.0之后版本生效
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).tintColor = UIColor.whiteColor()
} else {
//對9.0之前版本需橋接OC版對UIBarButtonItem的擴展
UIBarButtonItem.my_appearanceWhenContainedIn(UISearchBar.self).tintColor = UIColor.whiteColor()
}
二號坑:
searBar默認控制在輸入為空時鍵盤上搜索按鈕不可點擊,但是在向searchBar中粘貼字符串或者代碼控制直接像searchBar.text賦值,鍵盤上的搜索按鈕會依然處于失效狀態,此bug疑似由UISearchController內部機制引起
解決方案:
A.關閉searBar輸入為空時自動將搜索按鈕置失效,但需要根據需求自己控制輸入為空時是否允許觸發搜索
searchController.searchBar.enablesReturnKeyAutomatically = false
B.在向searchBar中粘貼字符串或者代碼控制直接向searchBar.text賦值時,對searchBar先resignFirstResponder再becomeFirstResponder
通過代碼控制直接向searchBar.text賦值時此方案已得到驗證,如何捕獲粘貼字符串動作本人目前未能實現,故尚未驗證,但是從前者的表現來看應該是可以的
代理實現
需要實現
1.UISearchBarDelegate
實現對鍵盤點擊搜索等動作的響應
//MARK: UISearchBarDelegate
extension SearchControllerForTableHeaderViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
//點擊鍵盤上的搜索按鈕時執行此代理,可按實際需求進行處理
print("didClickSearchBuuton")
}
}
2.UISearchControllerDelegate
控制在進入/離開搜索狀態時需要調整的設置和UI等
//MARK: UISearchControllerDelegate
extension SearchControllerForTableHeaderViewController: UISearchControllerDelegate {
func willPresentSearchController(searchController: UISearchController) {
//若需要在無輸入時亦展示searchResultsController.view,需執行此句,必須在主線程中執行
dispatch_async(dispatch_get_main_queue()) { () -> Void in
searchController.searchResultsController!.view.hidden = false;
}
}
func didPresentSearchController(searchController: UISearchController) {
//對于由代碼主動發起的searchController進入active狀態,需在此設置
searchController.searchBar.becomeFirstResponder()
}
}
3.UISearchResultsUpdating//根據searchResultsController是否為nil決定此代理由子MVC實現還是主MVC實現
//MARK: UISearchResultsUpdating
extension SearchResultsController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
if self.searchController == nil {
self.searchController = searchController
}
guard searchController.searchBar.text ?? "" != "" else {
return
}
searchKeywords(searchController.searchBar.text!)
}
}
示例項目傳送門
最后附上示例項目傳送門
https://github.com/LvJianting/UISearchControllerExample.git