UISearchController教程

本文翻譯自:http://www.raywenderlich.com/113772/uisearchcontroller-tutorial
教程使用iOS 9 SDKSwift 2進(jìn)行開發(fā)。


如果你的app展示了很多條數(shù)據(jù),并且需要在龐大的列表里滾動(dòng)來查閱,那么這種情況下敲街,比較友好的處理方式便是允許用戶進(jìn)行搜索。幸運(yùn)的是,UIKit包括UISearchBar能很好地集成進(jìn)UITableView并且能很快速地進(jìn)行過濾和響應(yīng)顶吮。
在這篇UISearchController教程中,我們會(huì)編譯一個(gè)含有搜索功能的Candy app粪薛,它建立于一個(gè)標(biāo)準(zhǔn)的table view悴了。我們會(huì)在table view中增加搜索的功能,包括了動(dòng)態(tài)過濾以及增加一個(gè)可選的范圍選擇,這所有的都是iOS 8新加入的UISearchController的優(yōu)勢(shì)湃交。

開始

可以從這兒下載初始項(xiàng)目熟空,這個(gè)demo已經(jīng)建立了一個(gè)navigation controller,啟動(dòng)它搞莺,我們會(huì)看到一個(gè)空的列表:


返回到Xcode息罗,Candy.swift這個(gè)文件包含了會(huì)被顯示的candy的所有信息,它實(shí)際上只有兩個(gè)屬性: namecategory才沧。
當(dāng)用戶搜索candy的時(shí)候迈喉,我們可以根據(jù)name進(jìn)行搜索,但是在教程最后我們也會(huì)通過category進(jìn)行范圍過濾温圆。

填充Table View

打開 MasterViewController.swift挨摸,candies屬性就是我們用來展示的供搜索的數(shù)據(jù)源,說到這岁歉,我們先創(chuàng)建一些candy得运!
在本教程中,我們只需要?jiǎng)?chuàng)建有限個(gè)數(shù)據(jù)保證能完成搜索功能就好锅移,在真正的生產(chǎn)app當(dāng)中熔掺,我們可能會(huì)需要成千上萬(wàn)條數(shù)據(jù)來進(jìn)行搜索。即便數(shù)據(jù)有那么多時(shí)非剃,我們的搜索方式還是一樣的置逻。
viewDidLoad()中,在super.viewDidLoad()后填充candies數(shù)組:

candies = [ 
    Candy(category:"Chocolate", name:"Chocolate Bar"),    
    Candy(category:"Chocolate", name:"Chocolate Chip"), 
    Candy(category:"Chocolate", name:"Dark Chocolate"), 
    Candy(category:"Hard", name:"Lollipop"), 
    Candy(category:"Hard", name:"Candy Cane"), 
    Candy(category:"Hard", name:"Jaw Breaker"), 
    Candy(category:"Other", name:"Caramel"), 
    Candy(category:"Other", name:"Sour Chew"), 
    Candy(category:"Other", name:"Gummi Bear")
]

再次編譯運(yùn)行app备绽,因?yàn)閠able view的代理和數(shù)據(jù)源方法已經(jīng)實(shí)現(xiàn)好诽偷,我們會(huì)看到數(shù)據(jù)正常顯示:



點(diǎn)擊一行cell并且能夠進(jìn)入到詳情頁(yè):


加入U(xiǎn)ISearchController

如果我們查閱UISearchController文檔,會(huì)發(fā)現(xiàn)文檔精簡(jiǎn)得令人發(fā)指疯坤,沒有進(jìn)行任何的搜索例子报慕,這個(gè)類只是簡(jiǎn)單的提供了一些標(biāo)準(zhǔn)接口來供開發(fā)者自己實(shí)現(xiàn)。
UISearchController用代理的模式來和app進(jìn)行溝通压怠,從而知道用戶在做些什么眠冈,我們必須自己寫一些函數(shù)來過濾字符串。
盡管這一開始顯得不是那么的友好菌瘫,但是自定義搜索方法給了我們對(duì)app更深的控制權(quán)蜗顽,我們的用戶會(huì)喜歡我們的搜索功能---因?yàn)閮?yōu)雅和快速。
如果大家曾經(jīng)開發(fā)過table view的搜索功能雨让,可能會(huì)發(fā)現(xiàn)這和UISearchDisplayController很像雇盖,但是自從iOS 8之后,這個(gè)類就被UISearchController取代了栖忠,因?yàn)槠浜?jiǎn)化了整個(gè)搜索流程崔挖。
不幸的是贸街,在寫這個(gè)教程的時(shí)候,Interface Builder并不支持UISearchController狸相,所以我們需要通過代碼來創(chuàng)建我們的UI薛匪。
MasterViewController.swift中,增加一個(gè)新的屬性:

let searchController = UISearchController(searchResultsController: nil)

通過參數(shù)searchResultsController傳nil來初始化UISearchController脓鹃,意思是我們告訴search controller我們會(huì)用相同的view來展示我們的搜索結(jié)果逸尖,如果我們想要指定一個(gè)不同的view controller,那就會(huì)被替代為顯示搜索結(jié)果瘸右。
下一步娇跟,我們需要為我們的searchController設(shè)置一些參數(shù)。仍然在MasterViewController.swift中太颤,在viewDidLoad()中加入以下代碼:

searchController.searchResultUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
  1. searchResultUpdaterUISearchController的一個(gè)屬性苞俘,它的值必須實(shí)現(xiàn)UISearchResultsUpdating協(xié)議,這個(gè)協(xié)議讓我們的類在UISearchBar文字改變時(shí)被通知到栋齿,我們之后會(huì)實(shí)現(xiàn)這個(gè)協(xié)議。
  2. 默認(rèn)情況下襟诸,UISearchController暗化前一個(gè)view瓦堵,這在我們使用另一個(gè)view controller來顯示結(jié)果時(shí)非常有用,但當(dāng)前情況我們并不想暗化當(dāng)前view歌亲。
  3. 設(shè)置definesPresentationContexttrue菇用,我們保證在UISearchController在激活狀態(tài)下用戶push到下一個(gè)view controller之后search bar不會(huì)仍留在界面上。
  4. 最后陷揪,我們?cè)黾?code>searchBar到我們的tableHeaderView中惋鸥。
UISearchResultsUpdating and Filtering

在設(shè)置完search controller后,我們需要再加些代碼來讓它工作悍缠,首先卦绣,加入如下屬性到MasterViewController:

var filterCandies = [Candy]()

這個(gè)屬性會(huì)保存用戶搜索后的結(jié)果,接著加入如下輔助方法:

func filterContentForSearchText(searchText: String, scope: String = "All) {
    filteredCandies = candies.filter { candy in
        return candy.name.lowercaseString.containsString(searchText.lowercaseString)
    }
    tableView.reloadData()
}

這個(gè)通過searchText的來對(duì)candies進(jìn)行過濾飞蚓,并且把結(jié)果記錄在filteredCandies中滤港,別擔(dān)心scope參數(shù),之后我們會(huì)用到的趴拧。
為了讓MasterViewController響應(yīng)這個(gè)search bar溅漾,我們需要實(shí)現(xiàn)UISearchResultsUpdating,打開** MasterViewController.swift**著榴,在主MasterViewController類之外再加上如下的extension:

extension MasterViewController: UISearchResultsUpdating {
    func updateSearchResultsForSearchController(searchController: UISearchController) {
        filterContentForSearchText(searchController.searchBar.text!)
    }
}

這個(gè)updateSearchResultsForSearchController(_:)方法是UISearchResultsUpdating中唯一一個(gè)我們必須實(shí)現(xiàn)的方法添履。
現(xiàn)在不管用戶輸入還是刪除search bar的text,UISearchController都會(huì)被通知到并執(zhí)行上述方法脑又。
filter()帶了一個(gè)(candy: Candy) -> Bool類型的閉包暮胧,這個(gè)方法遍歷數(shù)組里的所有數(shù)據(jù)锐借,然后調(diào)用這個(gè)閉包,傳入當(dāng)前的數(shù)組值叔壤。
我們用這個(gè)方法來判斷數(shù)據(jù)是否需要被搜索到展示給用戶瞎饲,如果需要我們返回true,否則返回false炼绘。
我們達(dá)到比較友好的體驗(yàn)嗅战,我們把searchTet以及原數(shù)據(jù)都小寫之后在進(jìn)行過濾。
編譯運(yùn)行程序俺亮,我們發(fā)現(xiàn)現(xiàn)在已經(jīng)有一個(gè)搜索框在列表上方了驮捍。
但不管怎么輸入,我們的搜索功能似乎不起作用脚曾,這僅僅是因?yàn)槲覀儧]有對(duì)過濾后的數(shù)據(jù)進(jìn)行顯示东且。
回到** MasterViewController.swift**,替換tableView(_:numberOfRowsInSection:)如下:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if sesarchController.active && searchController.searchBar.text != "" {
        return filteredCandies.count
    }
    return candies.count
}

接著替換tableView(_:cellForRowAtIndexPath:)方法:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
    let candy: Candy 
    if searchController.active && searchController.searchBar.text != "" {     
        candy = filteredCandies[indexPath.row] 
    } else { 
        candy = candies[indexPath.row] 
    } 
    cell.textLabel!.text = candy.name 
    cell.detailTextLabel!.text = candy.category 
    return cell
}

現(xiàn)在這些方法都會(huì)依賴searchControlleractive屬性來展示不同數(shù)據(jù)本讥,當(dāng)用戶點(diǎn)擊搜索區(qū)域內(nèi)的Search Bar時(shí)珊泳,active會(huì)自動(dòng)被設(shè)成true,如果search controller是active的拷沸,我們用看到用戶進(jìn)行搜索的結(jié)果色查。
編譯運(yùn)行程序,我們看到搜索成功了!


測(cè)試一段時(shí)間后撞芍,我們發(fā)現(xiàn)詳情頁(yè)有時(shí)會(huì)和點(diǎn)擊的不一致秧了,我們來修復(fù)它。

向詳情頁(yè)傳數(shù)據(jù)

在** MasterViewController.swift**文件中序无,找到perpareForSegue(_:sender:)方法验毡,找到如下代碼:

let candy = candies[indexPath.row]

之后替換這如下代碼:

let candy: Candy
if searchController.active && searchController.searchBar.text != "" {
    candy = filteredCandies[indexPath.row]
} else {
    candy = candies[indexPath.row]
}

一切正常了:


創(chuàng)建一個(gè)Scope Bar來對(duì)結(jié)果進(jìn)行再次過濾

如果我們希望能給用戶另一種過濾選擇,我們可以增加一個(gè)Scope Bar來過濾category字段帝嗡。我們用來過濾的categories包括Chocolate, Hard, Other晶通。
首先我們需要在MasterViewController中創(chuàng)建一個(gè)Scope bar,這個(gè)scope bar實(shí)際上是一個(gè)segmented control哟玷,來指定結(jié)果只能在某個(gè)范圍內(nèi)录择,在這個(gè)demo中我們的scope是category,但實(shí)際情況下碗降,scope可能是types, ranges或一些完全不同的東西隘竭。
要使用scope bar,我們需要再實(shí)現(xiàn)UISearchBarDelegate代理中的一個(gè)方法讼渊,加入如下extension:

extension MasterViewController: UISearchBarDelegate { 
    func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { 
        filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope]) 
    }
}

每當(dāng)用戶切換scope bar時(shí)动看,這個(gè)代理方法就會(huì)被調(diào)用,所以我們應(yīng)該在此用新的scope進(jìn)行重新搜索爪幻。
現(xiàn)在我們修改```filterContentForSearchText(_:scope:)來實(shí)現(xiàn)scope過濾這個(gè)功能:

func filterContentForSearchText(searchText: String, scope: String = "All") { 
    filteredCandies = candies.filter { candy in 
        let categoryMatch = (scope == "All") || (candy.category == scope) 
        return categoryMatch && candy.name.lowercaseString.containsString(searchText.lowercaseString    ) 
    }  
    tableView.reloadData()
}

我們還需要修改之前實(shí)現(xiàn)的updateSearchResultsForSearchController(_:):

func updateSearchResultsForSearchController(searchController: UISearchController) { 
    let searchBar = searchController.searchBar 
    let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex] 
    filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}

最后我們?cè)趕earch bar上加上scope bar菱皆,在** MasterViewController.swift**中须误,在viewDidLoad()中,設(shè)置search controller后加入:

searchController.searchBar.scopeButtonTitles = ["All", "Chocolate", "Hard", "Other"]
searchController.searchBar.delegate = self

編譯運(yùn)行程序:



搞定仇轻!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末京痢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子篷店,更是在濱河造成了極大的恐慌祭椰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疲陕,死亡現(xiàn)場(chǎng)離奇詭異方淤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹄殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門携茂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诅岩,你說我怎么就攤上這事讳苦。” “怎么了吩谦?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鸳谜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我逮京,道長(zhǎng)卿堂,這世上最難降的妖魔是什么束莫? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任懒棉,我火速辦了婚禮,結(jié)果婚禮上览绿,老公的妹妹穿的比我還像新娘策严。我一直安慰自己,他們只是感情好饿敲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布妻导。 她就那樣靜靜地躺著,像睡著了一般怀各。 火紅的嫁衣襯著肌膚如雪倔韭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天瓢对,我揣著相機(jī)與錄音寿酌,去河邊找鬼。 笑死硕蛹,一個(gè)胖子當(dāng)著我的面吹牛醇疼,可吹牛的內(nèi)容都是我干的硕并。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼秧荆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼倔毙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乙濒,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陕赃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后琉兜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凯正,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年豌蟋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廊散。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梧疲,死狀恐怖允睹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幌氮,我是刑警寧澤缭受,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站该互,受9級(jí)特大地震影響米者,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宇智,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一蔓搞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧随橘,春花似錦喂分、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至萝嘁,卻和暖如春梆掸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牙言。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工酸钦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嬉挡。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓钝鸽,卻偏偏與公主長(zhǎng)得像汇恤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拔恰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容