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