Swift - 使用UISearchController实现带搜索栏的表格
(本文代码已升级至Swift3)
我原来写过一篇文章“Swift - 带结果列表的搜索条(UISearchDisplayController)的用法”,当时是使用UISearchDisplayController来实现带有搜索功能的列表,由于UISearchDisplayController本身就整合了搜索条和表格,所有用起来很方便。
我原来写过一篇文章“Swift - 带结果列表的搜索条(UISearchDisplayController)的用法”,当时是使用UISearchDisplayController来实现带有搜索功能的列表,由于UISearchDisplayController本身就整合了搜索条和表格,所有用起来很方便。
到了iOS8,苹果废除UISearchDisplayController,建议我们使用UISearchController配合UITableView来实现。我们可以把搜索条放在表格头部,或者放在页面顶部,还是很灵活的。下面通过代码演示如何使用UISearchController实现具有搜索功能的表格。
效果图如下:

样例代码说明:
(1)这里对ViewController做了类扩展ViewControllerExtensions.swift,把UITableView和UISearchController的代理方法都写在扩展类里,使代码更加简洁。
(2)同时这里提供两个样例。分别对应两种搜索方式,一个是实时搜索(即每输入一个字符就搜索过滤一次结果)。一个是输入时不进行搜索,只有点击键盘上的搜索按钮后才开始搜索。
一、实时搜索
--- ViewController.swift ---
import UIKit
class ViewController: UIViewController {
//展示列表
var tableView: UITableView!
//搜索控制器
var countrySearchController = UISearchController()
//原始数据集
let schoolArray = ["清华大学","北京大学","中国人民大学","北京交通大学","北京工业大学",
"北京航空航天大学","北京理工大学","北京科技大学","中国政法大学",
"中央财经大学","华北电力大学","北京体育大学","上海外国语大学","复旦大学",
"华东师范大学","上海大学","河北工业大学"]
//搜索过滤后的结果集
var searchArray:[String] = [String](){
didSet {self.tableView.reloadData()}
}
override func viewDidLoad() {
super.viewDidLoad()
//创建表视图
let tableViewFrame = CGRect(x: 0, y: 20, width: self.view.frame.width,
height: self.view.frame.height-20)
self.tableView = UITableView(frame: tableViewFrame, style:.plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//创建一个重用的单元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "MyCell")
self.view.addSubview(self.tableView!)
//配置搜索控制器
self.countrySearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self //两个样例使用不同的代理
controller.hidesNavigationBarDuringPresentation = false
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.searchBarStyle = .minimal
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
--- ViewControllerExtensions.swift ---
import Foundation
import UIKit
extension ViewController: UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.countrySearchController.isActive {
return self.searchArray.count
} else {
return self.schoolArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
//为了提供表格显示性能,已创建完成的单元需重复使用
let identify:String = "MyCell"
//同一形式的单元格重复使用,在声明时已注册
let cell = tableView.dequeueReusableCell(withIdentifier: identify,
for: indexPath)
if self.countrySearchController.isActive {
cell.textLabel?.text = self.searchArray[indexPath.row]
return cell
} else {
cell.textLabel?.text = self.schoolArray[indexPath.row]
return cell
}
}
}
extension ViewController: UITableViewDelegate
{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension ViewController: UISearchResultsUpdating
{
//实时进行搜索
func updateSearchResults(for searchController: UISearchController) {
self.searchArray = self.schoolArray.filter { (school) -> Bool in
return school.contains(searchController.searchBar.text!)
}
}
}
二、点击搜索按钮后才进行搜索
--- ViewController.swift ---
import UIKit
class ViewController: UIViewController {
//展示列表
var tableView: UITableView!
//搜索控制器
var countrySearchController = UISearchController()
//原始数据集
let schoolArray = ["清华大学","北京大学","中国人民大学","北京交通大学","北京工业大学",
"北京航空航天大学","北京理工大学","北京科技大学","中国政法大学",
"中央财经大学","华北电力大学","北京体育大学","上海外国语大学","复旦大学",
"华东师范大学","上海大学","河北工业大学"]
//搜索过滤后的结果集
var searchArray:[String] = [String](){
didSet {self.tableView.reloadData()}
}
override func viewDidLoad() {
super.viewDidLoad()
//创建表视图
let tableViewFrame = CGRect(x: 0, y: 20, width: self.view.frame.width,
height: self.view.frame.height-20)
self.tableView = UITableView(frame: tableViewFrame, style:.plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//创建一个重用的单元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "MyCell")
self.view.addSubview(self.tableView!)
//配置搜索控制器
self.countrySearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchBar.delegate = self //两个样例使用不同的代理
controller.hidesNavigationBarDuringPresentation = false
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.searchBarStyle = .minimal
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
--- ViewControllerExtensions.swift ---
import Foundation
import UIKit
extension ViewController: UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.countrySearchController.isActive {
return self.searchArray.count
} else {
return self.schoolArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
//为了提供表格显示性能,已创建完成的单元需重复使用
let identify:String = "MyCell"
//同一形式的单元格重复使用,在声明时已注册
let cell = tableView.dequeueReusableCell(withIdentifier: identify,
for: indexPath)
if self.countrySearchController.isActive {
cell.textLabel?.text = self.searchArray[indexPath.row]
return cell
} else {
cell.textLabel?.text = self.schoolArray[indexPath.row]
return cell
}
}
}
extension ViewController: UITableViewDelegate
{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension ViewController: UISearchBarDelegate {
//点击搜索按钮
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.searchArray = self.schoolArray.filter { (school) -> Bool in
return school.contains(searchBar.text!)
}
}
//点击取消按钮
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.searchArray = self.schoolArray
}
}

你好,抱歉,我是新手入门。请问类拓展ViewControllerExtensions.swift是添加的那个文件。
你好,我想知道原始数据如果不是schoolArray 而是一个表的话应该怎么设置呢?
你好 可以把这个demo发我一份吗 2807338860@qq.com
你好 我添加这个的时候,报错
controller.searchResultsUpdater = self //两个样例使用不同的代理
运行时,编译到这里就闪退了
if self.countrySearchController.isActive
怎么解决
请问怎么取消实时搜索,改成通过点击键盘上的搜索按钮进行搜索。
您好
如果开启showsScopeBar,特别是将searchbar放到导航栏位置的话,显示会有问题,showsScopeBar整个被压在searchbar下面
再就是开启cancel按钮的话也会出现问题 最开始会显示 但如果点击搜索栏进入搜索模式后(搜索栏会自己弹出一个返回作用的cancel) 再返回去的话之前的cancel按钮就没有了
请问这两个问题有没有办法解决呢 谢谢
这样还不行,报如下错误
Connot invoke 'filter' with an argument lits of type '(@noescape(Element) throws -> Bool)'
语句为
self.requestSearch = self.requestData.filter(){$0.name.rangeOfString(searchController.searchBar.text!) != nil }
我的数据是这样的
let requestData = NSMutableArray()
requestData.addObject(TradeItem(name: "名称1", model: "款式", color: "颜色", size: "尺码"))
requestData.addObject(TradeItem(name: "名称2", model: "款式", color: "颜色", size: "尺码"))
requestData.addObject(TradeItem(name: "名称3", model: "款式", color: "颜色", size: "尺码"))
requestData.addObject(TradeItem(name: "名称4", model: "款式", color: "颜色", size: "尺码"))
requestData.addObject(TradeItem(name: "名称11", model: "款式", color: "颜色", size: "尺码"))
是定义的对象
搜索的内容 是一个对象 应该如何搜索?
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %@",
searchController.searchBar.text!)
let array = (self.schoolArray as NSArray)
.filteredArrayUsingPredicate(searchPredicate)
这段应该如何写》?
你理解错了
我要搜索框放在导航栏上
我的 ViewController 导航上加了UINavigationController 搜索框 没有在上面, 请问如何放上去?