Intermediate iOS 11 Programming with Swift (二): 在UITableView中添加區(qū)段和索引列表

本文是Intermediate iOS 11 Programming with Swift 4系列?的 第 二 篇.

2.0

如果您想在UITableView中顯示大量的記錄,您最好重新考慮如何顯示數(shù)據(jù)的方法祭衩。隨著行數(shù)的增加灶体,表視圖變得難以處理。改進(jìn)用戶體驗(yàn)的一種方法是將數(shù)據(jù)組織成部分掐暮。通過將相關(guān)數(shù)據(jù)分組在一起蝎抽,可以為用戶提供更好的訪問方式。

此外路克,還可以在表視圖中實(shí)現(xiàn)索引列表樟结。索引表視圖或多或少與純樣式表視圖相同。惟一的區(qū)別是它在表視圖的右邊包含一個(gè)索引精算。索引表在iOS應(yīng)用程序中非常常見瓢宦。最著名的例子是iPhone的內(nèi)置聯(lián)系人應(yīng)用。通過提供索引滾動灰羽,用戶可以立即訪問表的特定部分驮履,而無需滾動每個(gè)部分鱼辙。

讓我們看看如何向一個(gè)簡單的表應(yīng)用程序中添加節(jié)和索引列表。如果您對UITableView實(shí)現(xiàn)有基本的了解玫镐,那么添加節(jié)和索引列表并不太難倒戏。基本上恐似,您需要處理UITableViewDataSource協(xié)議中定義的這些方法:

numberofSections (in:)?方法 - 返回表視圖中所有的部分杜跷。通常,我們將section的數(shù)量設(shè)置為1矫夷。如果你想要有多個(gè)部分葛闷,把這個(gè)值設(shè)置為大于1的數(shù)字.

tableView(_ :titleForHeaderInsection:) 方法 - 返回不同部分的標(biāo)題。如果您不喜歡為部分分配標(biāo)題双藕,則此方法是可選的.

tableView(_ : numberOfRowsInSection:)? 方法 - 返回特定部分中的行總數(shù).

tableView(_ : CellForRowAt:)?方法——如果您知道如何在UITableView中顯示數(shù)據(jù)孵运,那么這種方法對您來說不應(yīng)該是陌生的。它返回特定部分的表數(shù)據(jù).

sectionIndexTitles( for :)??方法——返回出現(xiàn)在表視圖右邊的索引列表中的索引標(biāo)題蔓彩。例如,您可以返回包含從a到Z值的字符串?dāng)?shù)組.

tableView(_ : sectionForSectionIndexTitle: at: )?方法——返回當(dāng)用戶點(diǎn)擊特定索引時(shí)驳概,表視圖應(yīng)該跳轉(zhuǎn)到的部分索引.

沒有比給你看一個(gè)例子更好的解釋實(shí)現(xiàn)的方法了赤嚼。和往常一樣,我們將構(gòu)建一個(gè)Simple App顺又,它將使您對索引列表實(shí)現(xiàn)有更好的理解.

Demo App

首先更卒,讓我們快速瀏覽一下我們將要構(gòu)建的演示應(yīng)用程序。這是一個(gè)非常簡單的應(yīng)用程序稚照,在標(biāo)準(zhǔn)的表格視圖中顯示動物列表蹂空。這個(gè)應(yīng)用程序并沒有列出所有的動物,而是將它們分組到不同的區(qū)域果录,并顯示一個(gè)索引列表上枕,以便快速訪問.? 下面的屏幕截圖顯示了演示應(yīng)用程序的最終交付.

2.1 Demo App?


下載Xcode項(xiàng)目模板

演示的重點(diǎn)是實(shí)現(xiàn)部分和索引列表.

因此,您可以從這里下載項(xiàng)目模板.這個(gè)模板已經(jīng)包含了你需要的一切弱恒。如果構(gòu)建模板辨萍,您將有一個(gè)應(yīng)用程序在表視圖中顯示動物列表(但沒有節(jié)和索引)。稍后返弹,我們將修改應(yīng)用程序锈玉,將數(shù)據(jù)分組到節(jié)中,并向表中添加索引列表.

2.1?


顯示部分UITableView

好的,讓我們開始吧义起。如果您打開IndexTableDemo項(xiàng)目拉背,動物數(shù)據(jù)將在數(shù)組中定義:

let animals = ["Bear", "Black Swan", "Buffalo", "Camel", "Cockatoo", "Dog", "Donkey", "Emu", "Giraffe", "Greater Rhea", "Hippopotamus", "Horse", "Koala", "Lion", "Llama", "Manatus", "Meerkat", "Panda", "Peacock", "Pig", "Platypus", "Polar Bear", "Rhinoceros", "Seagull", "Tasmania Devil", "Whale", "Whale Shark", "Wombat"]

我們會根據(jù)動物名字的第一個(gè)字母將數(shù)據(jù)分成幾個(gè)部分。有很多方法可以做到這一點(diǎn)默终。一種方法是用字典手工替換動物數(shù)組椅棺,如下所示:

let animals: [String: [String]] = ["B" : ["Bear", "Black Swan", "Buffalo"],

? ? ? ? "C" : ["Camel", "Cockatoo"],

? ? ? ? "D" : ["Dog", "Donkey"],

? ? ? ? "E" : ["Emu"],

? ? ? ? "G" : ["Giraffe", "Greater Rhea"],

? ? ? ? "H" : ["Hippopotamus", "Horse"],

? ? ? ? "K" : ["Koala"],

? ? ? ? "L" : ["Lion", "Llama"],

? ? ? ? "M" : ["Manatus", "Meerkat"],

? ? ? ? "P" : ["Panda", "Peacock", "Pig", "Platypus", "Polar Bear"],

? ? ? ? "R" : ["Rhinoceros"],

? ? ? ? "S" : ["Seagull"],

? ? ? ? "T" : ["Tasmania Devil"],

? ? ? ? "W" : ["Whale", "Whale Shark", "Wombat"]]”

在上面的代碼中犁罩,我們把動物數(shù)組變成了字典。動物名字的第一個(gè)字母被用作鑰匙土陪。與相應(yīng)鍵相關(guān)聯(lián)的值是一個(gè)動物名稱數(shù)組昼汗。

我們可以手動創(chuàng)建字典,但是如果我們可以通過編程的方式從animals數(shù)組中創(chuàng)建索引鬼雀,那不是很好嗎?讓我們看看怎么做顷窒。

首先,在AnimalTableViewController類中聲明兩個(gè)實(shí)例變量:

var animalsDict = [String: [String] ] ()

var animalSectionTitles = [String] ()

我們初始化用于存儲動物的空字典和用于存儲表的節(jié)標(biāo)題的空數(shù)組源哩。章節(jié)標(biāo)題是動物名字的首字母(如B)鞋吉。

因?yàn)槲覀兿霃腶nimals數(shù)組生成一個(gè)字典,所以我們需要一個(gè)helper方法來處理生成励烦。在AnimalTableViewController類中插入以下方法:

在此方法中谓着,我們遍歷動物數(shù)組中的所有項(xiàng)。對于每一項(xiàng)坛掠,我們首先提取動物名字的第一個(gè)字母赊锚。要獲取特定位置的索引(例如string . index),您必須向字符串本身詢問startIndex屉栓,然后調(diào)用index方法以獲得所需的位置舷蒲。在本例中,目標(biāo)位置為1友多,因?yàn)槲覀冎粚Φ谝粋€(gè)字符感興趣牲平。

在Swift 3中,您使用字符串的substring(to:)方法來獲取一個(gè)新字符串域滥,該字符串包含直到給定索引為止的字符∽菔粒現(xiàn)在,在Swift 4中启绰,該方法已被棄用昂儒。相反,您可以使用如下這樣的下標(biāo)將字符串分割為子字符串:

let animalKey = String(animal[..<firstLetterIndex])

aimal[..< firstLetterIndex]? ?將動物字符串切片到指定的索引酬土。在上面的例子中荆忍,它意味著提取第一個(gè)字符。您可能想知道為什么我們需要使用字符串初始化來包裝返回的子字符串撤缴。在Swift 4中刹枉,當(dāng)您將一個(gè)字符串分割為一個(gè)子字符串時(shí),您將得到一個(gè)子字符串實(shí)例屈呕。它是一個(gè)臨時(shí)對象微宝,與原始字符串共享其存儲。為了將子字符串實(shí)例轉(zhuǎn)換為字符串實(shí)例虎眨,需要使用String()對其進(jìn)行包裝.

如前所述蟋软,動物名字的第一個(gè)字母被用作字典的鍵镶摘。字典的值是該特定鍵的動物數(shù)組。因此岳守,一旦我們獲得了這個(gè)鍵凄敢,我們要么創(chuàng)建一個(gè)新的動物數(shù)組,要么將這個(gè)項(xiàng)目附加到一個(gè)現(xiàn)有的數(shù)組中湿痢。這里我們展示了前四次迭代的animalsDict值:

迭代 #1: animalsDict["B"] = ["Bear"]

迭代?#2: animalsDict["B"] = ["Bear", "Black Swan"]

迭代?#3: animalsDict["B"] = ["Bear", "Black Swan", "Buffalo"]

迭代?#4: animalsDict["C"] = ["Camel"]

在完全生成animalsDict之后涝缝,我們可以從字典的鍵中檢索section標(biāo)題。

要檢索字典的鍵譬重,只需調(diào)用keys方法拒逮。但是,返回的鍵是無序的臀规。Swift的標(biāo)準(zhǔn)庫提供了一個(gè)名為sort的函數(shù)滩援,該函數(shù)根據(jù)所提供的排序閉包的輸出返回已知類型的值的排序數(shù)組。

閉包接受相同類型的兩個(gè)參數(shù)(在本例中是字符串)塔嬉,并返回一個(gè)Bool值玩徊,以聲明第一個(gè)值在第一個(gè)值被排序后是應(yīng)該出現(xiàn)在第二個(gè)值之前還是之后。如果第一個(gè)值出現(xiàn)在第二個(gè)值之前谨究,它應(yīng)該返回true佣赖。

編寫排序閉包的一種方法是:

animalSectionTitles = animalSectionTitles.sorted( by: { (s1:String, s2:String) -> Bool in

? ? ? ? ? ? return s1 < s2

? ? ? ? })

您應(yīng)該非常熟悉閉包表達(dá)式語法。在閉包的主體中记盒,我們比較兩個(gè)字符串值。如果第二個(gè)值大于第一個(gè)值外傅,則返回true纪吮。例如,s1的值是B, s2的值是E萎胰,因?yàn)锽小于E碾盟,閉包返回true,表示B應(yīng)該出現(xiàn)在E之前技竟。

如果您仔細(xì)閱讀前面的代碼片段冰肴,您可能會奇怪為什么我要這樣編寫排序閉包:

animalSectionTitles = animalSectionTitles.sorted(by: { $0 < $1 })

它是編寫內(nèi)聯(lián)閉包的一種快捷方式。這里$0和$1指的是第一個(gè)和第二個(gè)字符串參數(shù)榔组。如果您使用簡短的參數(shù)名稱熙尉,您可以省略幾乎所有的閉包,包括參數(shù)列表和關(guān)鍵字;您只需要編寫閉包的主體搓扯。

Swift 3提供了另一個(gè)排序函數(shù)sort检痰。這個(gè)函數(shù)和排序后的函數(shù)很相似。排序函數(shù)不是返回排序的數(shù)組锨推,而是對原始數(shù)組進(jìn)行排序铅歼。您可以用下面的代碼替換代碼行:

animalSectionTitles.sort(by: { $0 < $1 })

創(chuàng)建助手方法后公壤,更新viewDidLoad方法以調(diào)用它:

下一步,改變numberOfSections(in:)方法椎椰,并返回section的總數(shù):



要在每個(gè)部分顯示標(biāo)題標(biāo)題厦幅,我們需要實(shí)現(xiàn)tableView(_:titleForHeaderInSection:)方法。每次顯示一個(gè)新節(jié)時(shí)慨飘,都會調(diào)用此方法确憨。基于給定的section索引套媚,我們返回相應(yīng)的section標(biāo)題

這很簡單,對吧?接下來缚态,我們必須告訴表視圖特定部分中的行數(shù)。在AnimalTableViewController中更新tableView(_:numberOfRowsInSection:)方法.

當(dāng)應(yīng)用程序開始在表視圖中呈現(xiàn)數(shù)據(jù)時(shí)堤瘤,每當(dāng)顯示新節(jié)時(shí)玫芦,就調(diào)用tableView(_:numberOfRowsInSection:)方法。根據(jù)節(jié)索引本辐,我們可以獲取節(jié)標(biāo)題并將其用作檢索該節(jié)的動物名稱的鍵桥帆。然后我們返回該部分的動物名稱的總數(shù)。在上面的代碼中慎皱,我們使用guard關(guān)鍵字來確定字典是否為特定的animalKey返回一個(gè)有效的數(shù)組老虫。如果不是,我們返回0.

在這種情況下茫多,guard關(guān)鍵字特別有用祈匙。在繼續(xù)執(zhí)行之前,我們希望確保animalValues包含一些值天揖。而且夺欲,它使代碼更清晰、更可讀今膊。

后些阅,修改tableView(_:cellForRowAt:)方法如下:



indexPath參數(shù)包含當(dāng)前行號,以及當(dāng)前的節(jié)索引斑唬。因此市埋,基于section索引,我們檢索section title(例如:“B”)并使用它作為檢索該部分動物名稱的鍵恕刘。代碼的其余部分非常簡單缤谎。我們只需獲取動物的名字并將其設(shè)置為細(xì)胞標(biāo)簽。imageFilename變量的計(jì)算方法是將動物名稱轉(zhuǎn)換為小寫字母褐着,然后用下劃線替換所有出現(xiàn)的空格弓千。

好了,你準(zhǔn)備好了!點(diǎn)擊Run按鈕献起,你會得到一個(gè)包含部分但沒有索引列表的應(yīng)用洋访。

向UITableView 添加列表索引?

那么镣陕,如何向表視圖添加索引列表呢? 同樣,這比您想象的要簡單姻政,并且只需幾行代碼就可以實(shí)現(xiàn)呆抑。只需添加sectionindextitle (for:)方法并返回一個(gè)section索引數(shù)組。這里我們將使用章節(jié)標(biāo)題作為索引汁展。

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {

? ? return animalSectionTitles

}

就是這樣! 重新編譯并運(yùn)行應(yīng)用程序鹊碍。您應(yīng)該在表的右邊找到索引。有趣的是食绿,您不需要任何實(shí)現(xiàn)侈咕,而且索引已經(jīng)工作了!嘗試點(diǎn)擊任何索引,您將被帶到表的特定部分.

2.3


添加 A -- Z 列表索引??

看來我們什么都做過了器紧。但是為什么我們一開始就提到了tableView(_:sectionForSectionIndexTitle:at:)方法呢?

目前耀销,索引列表不包含整個(gè)字母表。它只顯示那些被定義為動物字典的鍵的字母铲汪。有時(shí)熊尉,您可能希望在索引列表中顯示A-Z。讓我們在animaltableviewcontroller中聲明一個(gè)名為animalindextitle的新變量:

let animalIndexTitles = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

接下來掌腰,更改sectionIndexTitle (for:)方法并返回animalIndexTitle數(shù)組狰住,而不是animalSectionTitle數(shù)組

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {

? ? return animalIndexTitles

}

現(xiàn)在,重新編譯并運(yùn)行這個(gè)應(yīng)用程序齿梁。太酷了!該應(yīng)用程序?qū)⑺饕龔腁顯示到Z催植。

但是等一下,它不能正常工作!如果你嘗試點(diǎn)擊索引“C”勺择,應(yīng)用程序會跳轉(zhuǎn)到“D”部分查邢。如果你點(diǎn)擊索引“G”,它會指向“K”部分酵幕。下面顯示新舊索引之間的映射。

好,你可能會注意到,索引的數(shù)量大于部分的數(shù)量,和UITableView對象不知道如何處理索引缓苛。您的職責(zé)是實(shí)現(xiàn)tableView(_:sectionForSectionIndexTitle:at:)方法芳撒,并在選中特定索引時(shí)顯式地告訴表視圖區(qū)段編號。增加以下新方法:

override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {

? ? guard let index = animalSectionTitles.index(of: title) else {

? ? ? ? return -1

? ? }

? ? return index

}

基于所選的索引名(即title)未桥,我們定位了animalsectiontitle的正確節(jié)索引笔刹。在Swift中,您使用名為index(of:)的方法來查找數(shù)組中特定項(xiàng)的索引冬耿。

實(shí)現(xiàn)的整個(gè)要點(diǎn)是驗(yàn)證給定的標(biāo)題是否可以在animalsectiontitle數(shù)組中找到舌菜,并返回相應(yīng)的索引。然后亦镶,表視圖移動到相應(yīng)的部分日月。例如袱瓮,如果標(biāo)題是B,我們檢查B是否是有效的節(jié)標(biāo)題并返回索引1爱咬。如果找不到標(biāo)題(例如A)尺借,我們返回-1。

重新編譯并運(yùn)行應(yīng)用程序精拟。索引列表現(xiàn)在應(yīng)該可以工作了.

定制部分標(biāo)題

通過覆蓋UITableView類和UITableViewDelegate協(xié)議中定義的一些方法燎斩,您可以輕松地定制節(jié)頭。在這個(gè)演示中蜂绎,我們將做一些簡單的更改:?

更改節(jié)標(biāo)題的高度?

更改節(jié)標(biāo)題的字體和背景顏色?

要更改區(qū)段標(biāo)題的高度栅表,您可以簡單地覆蓋tableView(_:heightForHeaderInSection:)方法并返回首選的高度.

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

? ? return 50

}

在顯示節(jié)頭視圖之前,將調(diào)用tableView(_:willDisplayHeaderView:forSection:)方法师枣。該方法包含一個(gè)名為view的參數(shù)怪瓶。這個(gè)視圖對象可以是自定義頭視圖,也可以是標(biāo)準(zhǔn)頭視圖坛吁。在我們的演示, 我們只使用標(biāo)準(zhǔn)的header視圖劳殖,它是UITableViewHeaderFooterView對象。一旦有了header視圖拨脉,就可以更改文本顏色哆姻、字體和背景顏色。

再次運(yùn)行應(yīng)用程序玫膀。頭視圖應(yīng)該用您喜歡的字體和顏色進(jìn)行更新矛缨。


2.5?


總結(jié)?

當(dāng)您需要顯示大量記錄時(shí),將數(shù)據(jù)組織到節(jié)中并提供索引列表以方便訪問是簡單而有效的帖旨。在本章中箕昭,我們已經(jīng)介紹了索引表的實(shí)現(xiàn)。現(xiàn)在解阅,我相信您應(yīng)該知道如何向您的表視圖添加區(qū)段和索引列表.

你可以在這里下載原作者的 Xcode 項(xiàng)目.?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末落竹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子货抄,更是在濱河造成了極大的恐慌述召,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟹地,死亡現(xiàn)場離奇詭異积暖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)怪与,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門夺刑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事遍愿〈嬉” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵错览,是天一觀的道長纫雁。 經(jīng)常有香客問我,道長倾哺,這世上最難降的妖魔是什么轧邪? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮羞海,結(jié)果婚禮上忌愚,老公的妹妹穿的比我還像新娘。我一直安慰自己却邓,他們只是感情好硕糊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著腊徙,像睡著了一般简十。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撬腾,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天螟蝙,我揣著相機(jī)與錄音,去河邊找鬼民傻。 笑死胰默,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的漓踢。 我是一名探鬼主播牵署,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喧半!你這毒婦竟也來了奴迅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挺据,失蹤者是張志新(化名)和其女友劉穎取具,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吴菠,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年浩村,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了做葵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡心墅,死狀恐怖酿矢,靈堂內(nèi)的尸體忽然破棺而出榨乎,到底是詐尸還是另有隱情,我是刑警寧澤瘫筐,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布蜜暑,位于F島的核電站,受9級特大地震影響策肝,放射性物質(zhì)發(fā)生泄漏肛捍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一之众、第九天 我趴在偏房一處隱蔽的房頂上張望拙毫。 院中可真熱鬧,春花似錦棺禾、人聲如沸缀蹄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缺前。三九已至,卻和暖如春悬襟,著一層夾襖步出監(jiān)牢的瞬間衅码,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工古胆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肆良,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓逸绎,卻偏偏與公主長得像惹恃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子棺牧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

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