翻譯:UISearchController Tutorial: Getting Started

本文譯自:UISearchController Tutorial: Getting Started

當(dāng)一個app要顯示大量的數(shù)據(jù),滑動列表并不會讓人愉悅拴签。所以允許用戶搜索指定的內(nèi)容變得刻不容緩。
好消息是前塔,UIKit已經(jīng)將UISearchBar和UITableView無縫結(jié)合在一起了勺鸦。

在本教程中,你將用標(biāo)準(zhǔn)的table view創(chuàng)建一個可以搜索糖果的app舶胀。
使用iOS8的新特性UISearchController,賦予table view搜索的功能碧注,包含動態(tài)過濾嚣伐,
還要添加一個可供選擇的scope bar。
最后萍丐,學(xué)會如何讓app更加友好轩端,滿足用戶的需求。

UISearchController-Square.png

開始

點擊這里下載初始項目并打開它逝变,
這個項目已經(jīng)有了一個帶樣式的navigation controller基茵。運行它,你將看到一個空的列表:

UISearchController-Starter-281x500.png

回到Xcode壳影,文件Candy.swift中有一個類用來保存每一個糖果的信息拱层,
這個類有兩個屬性,分別對應(yīng)糖果的名稱和種類宴咧。
當(dāng)用戶使用你的app搜索糖果時根灯,你將根據(jù)用戶輸入的文字定位到對應(yīng)的那一項。
在教程的最后你要實現(xiàn)一個Scope Bar掺栅,到時你就明白種類字符串有多重要烙肺。

創(chuàng)建Table View

打開 MasterViewController.swiftcandies屬性用來管理所有不同的Candy對象.
說到這氧卧,是時候創(chuàng)建一些Candy了桃笙。

在本教程中,你只需要少量的數(shù)據(jù)來演示search bar是如何工作的沙绝;
在正式的項目中搏明,你也許有幾千個對象要被搜索鼠锈。
不論是幾千條還是幾條數(shù)據(jù),這個方法都同樣適用熏瞄。

創(chuàng)建candies數(shù)據(jù)脚祟,將下面的代碼添加到viewDidLoad()方法中,然后call super.viewDidLoad()

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")
]

再運行一次你的項目强饮,table view的delegate和datasource方法已經(jīng)實現(xiàn)了由桌,
你將看到一個有數(shù)據(jù)的table view:

UISearchController-Data-281x500.png

選擇一行后會展示相應(yīng)的糖果詳細:

darkchocolate.png

糖果太多了,需要一些時間才能找到想要到邮丰!你需要一個 UISearchBar行您。

引入 UISearchController

如果你看過UISearchController的文檔,你會發(fā)現(xiàn)它很懶剪廉。它沒有做任何關(guān)于搜索的工作娃循。
這個類簡單地提供一個用戶期望的標(biāo)準(zhǔn)接口。

UISearchController通過委托告訴app用戶正在做什么斗蒋。
你需要自己編寫所有的字符串匹配函數(shù)捌斧。

雖然看起來有點嚇人,編寫自定義搜索函數(shù)對返回的數(shù)據(jù)進行嚴(yán)格的控制泉沾,
你的用戶也會感到搜索非常智能和快速捞蚂。

如果你使用過iOS的table view搜索,你也許很熟悉UISearchDisplayController跷究。
從iOS8開始姓迅,這個類被UISearchController替代了,并簡化了搜索過程俊马。

不幸的是丁存,在撰寫本文時,Interface Builder不支持UISearchController柴我,所以要用代碼來制作UI解寝。

MasterViewController.swift中添加一個屬性:

let searchController = UISearchController(searchResultsController: nil)

初始化UISearchController時并沒有設(shè)置searchResultsController打月,
你告訴search controller要使用默認的視圖用來展示搜索結(jié)果绑谣。
如果你指定一個不同的viewController茅撞,那么它將被用來展示結(jié)果尼摹。

下一步,需要給searchController設(shè)置一些參數(shù)薯定。
依然在MasterViewController.swift中,添加下面的代碼到viewDidLoad()

searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar

下面是代碼的說明:

  1. searchResultsUpdaterUISearchController中的一個屬性,遵循了協(xié)議UISearchResultsUpdating晕窑。
    這個協(xié)議允許類接收UISearchBar文本變化的通知。過一會就要使用這個協(xié)議卵佛。
  2. 默認情況下杨赤,UISearchController會將presented視圖變暗敞斋。
    當(dāng)你使用另一個viewController作為searchResultsController會非常有用,
    在現(xiàn)在的實例中疾牲,你已經(jīng)設(shè)置了當(dāng)前的view來展示結(jié)果植捎,所以不需要讓它變暗。
  3. 通過設(shè)置definesPresentationContexttrue阳柔,能夠確保UISearchController被激活時焰枢,
    用戶跳轉(zhuǎn)到另一個viewController,而search bar依然保留在屏幕上舌剂。
  4. 最后济锄,將searchBar添加到table view的tableHeaderView
    記住霍转,Interface Builder還不兼容UISearchController荐绝,這一步是必須的。

UISearchResultsUpdating和Filtering

設(shè)置了search controller后避消,還需要寫一些代碼讓它工作起來低滩。
首先,將下面的屬性添加到MasterViewController的頂部:

var filteredCandies = [Candy]()

這個屬性將持有用戶正在搜索的糖果對象岩喷。
下一步恕沫,將這個方法添加到MasterViewController

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

tableView.reloadData()
}

這個方法會根據(jù)searchText過濾candies,并將結(jié)果添加到filteredCandies均驶。
不要擔(dān)心scope這個參數(shù)昏兆,下一節(jié)就會用到它。

為了讓MasterViewController響應(yīng)search bar妇穴,必須實現(xiàn)UISearchResultsUpdating爬虱。
打開MasterViewController.swift,添加下面的類擴展腾它,在MasterViewController類外面:

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

updateSearchResultsForSearchController(_:)UISearchResultsUpdating協(xié)議中唯一一個而且是必須實現(xiàn)的方法跑筝。

現(xiàn)在,無論用戶怎樣修改search bar的文本瞒滴,UISearchController都會通過這個方法告訴MasterViewController曲梗。
這個方法簡單的調(diào)用了助手方法,并將search bar當(dāng)前的文本作為參數(shù)妓忍。

filter()用到了(candy: Candy) -> Bool類型的閉包虏两。它會循環(huán)數(shù)組中的每一個元素,然后當(dāng)前的元素發(fā)給閉包世剖。

使用它來確定一個糖果是否作為搜索結(jié)果來呈現(xiàn)給用戶定罢。
返回true將當(dāng)前的糖果添加到filtered數(shù)組中,否則返回false旁瘫。

為了判斷結(jié)果祖凫,containsString(_:)用來檢查candy的name是否包含了searchText琼蚯。
但在比較之前,使用lowercaseString方法將字符串轉(zhuǎn)換成小寫惠况。

注意:大多數(shù)時候遭庶,用戶不會去在乎輸入的大小寫問題,

如果大小寫不匹配的話稠屠,依然不會返回結(jié)果峦睡。

現(xiàn)在,你輸入"Chocolate"或者"chocolate"都會返回匹配的結(jié)果权埠。這太有用了!!

再運行一次赐俗,你會發(fā)現(xiàn)table上面有一個search bar。

然而弊知,輸入任何文本都不會呈現(xiàn)過濾的結(jié)果阻逮。什么鬼?
這只是因為寫的代碼還沒有告訴table何時使用過濾后的數(shù)據(jù)秩彤。

回到MasterViewController.swift叔扼,將tableView(_:numberOfRowsInSection:)替換成下面的代碼:

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

沒有太多的修改,僅僅檢查了一下用戶是否正在輸入漫雷,
并使用過濾后或者正常的數(shù)據(jù)給table瓜富。

接下來,將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
}

這兩個方法都使用了searchControlleractive屬性來決定呈現(xiàn)哪個數(shù)組降盹。

當(dāng)用戶點擊Search Bar的文本框与柑,active會自動設(shè)置為true
如果search controller處在激活狀態(tài)蓄坏,會看到用戶已經(jīng)輸入了一些文字价捧。
如果已經(jīng)存在了,便會返回filteredCandies的數(shù)據(jù)涡戳,否則返回完成列表的數(shù)據(jù)结蟋。

回想一下,search controller會自動顯示或隱藏table渔彰,所有代碼要做的就是根據(jù)用戶的輸入提供正確的數(shù)據(jù)嵌屎。

ragecomic.png

編譯運行一下,你有一個Search Bar來過濾數(shù)據(jù)恍涂。

UISearchController-Filter-281x500.png

試試這個app宝惰,已經(jīng)可以搜索到各種糖果了。

這里還有一個問題再沧,選中一個搜索結(jié)果尼夺,會發(fā)現(xiàn)詳情界面顯示了錯誤的糖果!來修復(fù)它。

發(fā)送數(shù)據(jù)給Detail View

在將數(shù)據(jù)發(fā)送給詳細視圖時汞斧,需要確保控制器需要知道哪個上下文正在使用:是完整的列表還是搜索結(jié)果什燕。
還是在MasterViewController.swift粘勒,在prepareForSegue(_:sender:)中找到下面的代碼:

let candy = candies[indexPath.row]

替換成:

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

這里執(zhí)行了tableView(_:numberOfRowsInSection:)tableView(_:cellForRowAtIndexPath:)相同的檢查,
但現(xiàn)在提供了正確的糖果對象給詳細視圖屎即。

再次運行一遍庙睡,看看是不是正確的。

CandySearch-DetailView-281x500.png

使用Scope Bar來篩選數(shù)據(jù)

還有另一種方法過濾數(shù)據(jù)技俐,添加一個Scope Bar根據(jù)糖果的類別來過濾乘陪。
類別就是創(chuàng)建Candy對象時添加的,如Chocolate雕擂,Hard啡邑,Other

先在MasterViewController里面添加一個scope bar井赌。
scope bar是一個分段控件谤逼,用來縮小搜索的范圍。
范圍就是最初定義的仇穗。在這個項目中流部,范圍就是糖果的種類,但也可以是其他的纹坐。

先來實現(xiàn)scope bar的代理方法枝冀。

MasterViewController.swift中,添加另一個擴展UISearchBarDelegate耘子,
將下面的代碼添加到UISearchResultsUpdating的后面:

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

這個代理方法會在用戶切換scope bar的時候通知viewController果漾,
當(dāng)它觸發(fā)時,之行了搜索方法filterContentForSearchText(_:scope:)谷誓。

修改filterContentForSearchText(_:scope:)方法支持范圍的選擇:

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()
}

只有當(dāng)參數(shù)scope等于“ALL”或者等于糖果對象的種類屬性才將candy添加到filteredCandies數(shù)組跨晴。

已經(jīng)快完成了,但范圍過濾還沒有生效片林。
需要修改方法updateSearchResultsForSearchController(_:)

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

現(xiàn)在唯一的問題就是還沒有scope bar控件端盆!
選擇文件MasterViewController.swift,在viewDidLoad()中添加下面的代碼:

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

這會給搜索欄添加一個分段控件费封,還有和candy的categories相對應(yīng)的標(biāo)題焕妙。
還包括一個名為“ALL”的分類,它將忽略種類的過濾弓摘。

UISearchController-Filter-with-Scope-281x500.png

何去何從

恭喜你焚鹊,已經(jīng)有了一個可以搜索的table view。
點擊這里下載完整的項目代碼韧献。

越來越多的app都使用了表格視圖末患,搜索功能成了標(biāo)配研叫。
沒理由不使用UISearechBarUISearchController

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末璧针,一起剝皮案震驚了整個濱河市嚷炉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌探橱,老刑警劉巖申屹,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隧膏,居然都是意外死亡哗讥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門胞枕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杆煞,“玉大人,你說我怎么就攤上這事腐泻∷餍鳎” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵贫悄,是天一觀的道長瑞驱。 經(jīng)常有香客問我,道長窄坦,這世上最難降的妖魔是什么唤反? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鸭津,結(jié)果婚禮上彤侍,老公的妹妹穿的比我還像新娘。我一直安慰自己逆趋,他們只是感情好盏阶,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闻书,像睡著了一般名斟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魄眉,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天砰盐,我揣著相機與錄音,去河邊找鬼坑律。 笑死岩梳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冀值,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼也物,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了列疗?” 一聲冷哼從身側(cè)響起滑蚯,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎作彤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乌逐,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡竭讳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浙踢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绢慢。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洛波,靈堂內(nèi)的尸體忽然破棺而出胰舆,到底是詐尸還是另有隱情,我是刑警寧澤蹬挤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布缚窿,位于F島的核電站,受9級特大地震影響焰扳,放射性物質(zhì)發(fā)生泄漏倦零。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一吨悍、第九天 我趴在偏房一處隱蔽的房頂上張望扫茅。 院中可真熱鬧,春花似錦育瓜、人聲如沸葫隙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恋脚。三九已至,卻和暖如春焰手,著一層夾襖步出監(jiān)牢的瞬間慧起,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工册倒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚓挤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像灿意,于是被迫代替她去往敵國和親估灿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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