如果你已經(jīng)動(dòng)手寫(xiě)過(guò)Swift的程序,相信你已經(jīng)了解了Swift語(yǔ)言的知識(shí),比如如何寫(xiě)類(class)和結(jié)構(gòu)體(struct)。但Swift可沒(méi)這么簡(jiǎn)單,呵呵呵。這篇教程主要講述Swift的一個(gè)強(qiáng)力的特性:泛型。這個(gè)特性在很多程序設(shè)計(jì)語(yǔ)言里都非常受歡迎。
對(duì)于類型安全(type-safe)語(yǔ)言,一個(gè)常見(jiàn)的問(wèn)題就是如何編寫(xiě)適用于多種類型輸入的程序。想象一下,兩個(gè)整型數(shù)相加和兩個(gè)浮點(diǎn)數(shù)相加的程序看起來(lái)應(yīng)該非常類似,甚至一模一樣才對(duì)。唯一的區(qū)別就是變量的類型不同。
在強(qiáng)類型語(yǔ)言中,你需要去定義諸如addInts, addFloats, addDoubles 等方法來(lái)正確地處理參數(shù)及返回值。
許多編程語(yǔ)言已經(jīng)解決了這個(gè)問(wèn)題。例如,在C++中,使用Template來(lái)解決。而Swift,Java和C#則采用了泛型來(lái)解決這個(gè)問(wèn)題。泛型,也是這篇文章要重點(diǎn)介紹的。
在這篇文章中,你將會(huì)學(xué)到Swift中如何使用泛型,也許你已經(jīng)接觸過(guò),也許沒(méi)有,不過(guò)沒(méi)關(guān)系,我們會(huì)來(lái)一一探索。然后,我們會(huì)創(chuàng)建一個(gè)Flicker圖片搜索應(yīng)用,這個(gè)應(yīng)用使用了自定義的泛型數(shù)據(jù)結(jié)構(gòu)來(lái)保存用戶搜索的內(nèi)容。
備注:本文假設(shè)你已經(jīng)對(duì)Swift有基本的了解或者有過(guò)Swift開(kāi)發(fā)經(jīng)驗(yàn)。如果你第一次接觸Swift或者對(duì)Swift不是太了解,建議你首先閱讀下other Swift tutorials。
泛型介紹
也許你不知道這個(gè)術(shù)語(yǔ),但相信你已經(jīng)在Swift中見(jiàn)到它了。Swift中的數(shù)組和字典類型就是使用泛型的經(jīng)典例子。
Object-C開(kāi)發(fā)者已經(jīng)習(xí)慣使用數(shù)組和字典去保存多種數(shù)據(jù)類型。這種方式提供了很大的靈活性,但是誰(shuí)又能知道一個(gè)API返回的數(shù)組里面到底是啥(數(shù)據(jù)類型)呢?你唯一能做的就是查看文檔或者查看(方法的)變量命令(這也是另外一種文檔喲!)。即使你查看了文檔,你也不能保證程序在運(yùn)行期不產(chǎn)生bug或者其他異常。
相比Object-C,Swift中的數(shù)組和字典都是類型安全的。一個(gè)Int型數(shù)組只可以保存Int而不可以保存String。這意味著你不用再查看文檔啦,編譯器就可以幫你做類型檢查,然后你就就快可以愉快地coding了!
例如,在Object-C的UIKit中, 在自定義的View里面處理觸摸事件可以這么寫(xiě):
上述方法里面的set只可以保存UITouch實(shí)例, 因?yàn)槲臋n里面就是這么說(shuō)的。由于這個(gè)集合里面可以放任何對(duì)象,所以你需要在代碼里面進(jìn)行類型轉(zhuǎn)換,也就是說(shuō)把touches里面的對(duì)象轉(zhuǎn)為UITouch對(duì)象。
當(dāng)前Swift的標(biāo)準(zhǔn)庫(kù)里面沒(méi)有定義集合對(duì)象,但是你可以使用數(shù)組來(lái)代替集合對(duì)象,你可以用swift重寫(xiě)上面的代碼:
上面的代碼明確告訴你 touches數(shù)組只可以包含 UITouch實(shí)例, 否則編譯器就會(huì)報(bào)異常。這樣一來(lái),你就再不用去做那煩人的類型轉(zhuǎn)換了,因?yàn)榫幾g器給你做了類型安全檢查,保證數(shù)組里面只允許有 UITouch對(duì)象。
簡(jiǎn)要說(shuō)來(lái),泛型為類提供了一個(gè)類型參數(shù)。所有的數(shù)組都有相同的作用,即按順序儲(chǔ)存變量的數(shù)值,泛型數(shù)組除了多了一個(gè)類型參數(shù)之外,沒(méi)有其他的不同之處。或許這樣想更容易理解:你將要應(yīng)用在數(shù)組上的各種算法和儲(chǔ)存的數(shù)值類型無(wú)關(guān),因此這些算法對(duì)于泛型數(shù)組和非泛型數(shù)組都適用。
既然你已經(jīng)明白了泛型的基礎(chǔ)知識(shí)和用法,那我們就開(kāi)始把它應(yīng)用在一個(gè)具體的例子上吧。
泛型實(shí)例
為了測(cè)試泛型,你將會(huì)編寫(xiě)一個(gè)在Flickr上搜索圖片的應(yīng)用。
首先請(qǐng)下載這個(gè)程序雛形,并盡快熟悉里面主要的類。其中Flickr類用于和Flickr的API交互。請(qǐng)注意這個(gè)類里面包含了一個(gè)API key(通常用于用戶授權(quán)—譯者注),但如果你想要擴(kuò)展這個(gè)應(yīng)用的話可能需要用自己的key,注冊(cè)請(qǐng)點(diǎn)我。
構(gòu)造并運(yùn)行這個(gè)應(yīng)用,你會(huì)看到這個(gè):
好像什么都沒(méi)有?別急,用不了多久你就可以讓它幫你抓取可愛(ài)的喵圖了!
有序字典(原文Ordered Dictionaries)
你的應(yīng)用會(huì)根據(jù)每個(gè)用戶的查詢情況下載圖片,按照?qǐng)D片被搜索到的頻率由高到低排序并顯示。
但如果用戶對(duì)同樣的關(guān)鍵字搜索了兩次會(huì)怎樣?如果這個(gè)應(yīng)用能顯示上次搜索的結(jié)果就好了。
或許用數(shù)組來(lái)實(shí)現(xiàn)這個(gè)功能也行得通,但為了學(xué)習(xí)泛型,你將會(huì)使用一個(gè)全新的數(shù)據(jù)結(jié)構(gòu):有序字典。
和數(shù)組不同的是,包括Swift在內(nèi)地很多編程語(yǔ)言和框架都不保證集合(sets)和字典(dictionaries)的數(shù)據(jù)存儲(chǔ)順序。有序字典和普通的字典類似,不同之處在于它的key是有序的。你將會(huì)用這個(gè)特性,根據(jù)搜索關(guān)鍵字按順序存儲(chǔ)搜索結(jié)果。這樣存儲(chǔ)的好處是可以快速查詢并更新圖片列表。
一個(gè)草率的想法是自定義一個(gè)數(shù)據(jù)結(jié)構(gòu)處理有序字典。但是你需要更加有前瞻性才行!你必須考慮到如何讓你的應(yīng)用在未來(lái)幾年內(nèi)都能正常工作!因此在這里使用泛型再合適不過(guò)了。
初始數(shù)據(jù)結(jié)構(gòu)
點(diǎn)擊“文件/新建/文件...”新建一個(gè)文件,并選擇“IOS/Source/Swift File”。點(diǎn)擊“下一步”并把這個(gè)文件命名為“OrderedDictionary”。最后,點(diǎn)擊“創(chuàng)建”。
你會(huì)得到一個(gè)空的Swift文件,加這樣一段代碼進(jìn)去:
到現(xiàn)在為止應(yīng)該都沒(méi)有什么問(wèn)題。通過(guò)語(yǔ)義可以看出這個(gè)對(duì)象是一個(gè)結(jié)構(gòu)體。
注意:總之,值的語(yǔ)義可以想象為“復(fù)制、粘貼的行為”,而不是“分享、參考的行為”。值的語(yǔ)義帶來(lái)一系列的好處,例如不用擔(dān)心一段代碼無(wú)意地修改你的數(shù)據(jù)。了解更多,點(diǎn)擊"Swift by Tutorials"的第三章節(jié):類和結(jié)構(gòu)體。
現(xiàn)在你需要將其一般化,以便它能夠裝載你需要的任何類型的數(shù)據(jù)。通過(guò)下列改變你對(duì)Swift中“結(jié)構(gòu)”的定義:
在尖括弧中的元素是通用類型的參數(shù)。KeyType和ValueType不是他們自身的類型,而是你可以使用在結(jié)構(gòu)里定義取代的類型。現(xiàn)在就簡(jiǎn)潔清新許多了!
最簡(jiǎn)單的實(shí)現(xiàn)一個(gè)有順序的字典是保持一個(gè)數(shù)組和一個(gè)字典。字典中將會(huì)裝載衍射,而數(shù)組將裝載keys的順序。
在結(jié)構(gòu)體內(nèi)部的定義中,加入以下的代碼:
這樣聲明有兩個(gè)目的,就像上例描述的,有兩種類型的用于給已經(jīng)存在的類型的取新的名稱的別名。在這,你將分別地為后面的數(shù)組和字典賦值了別名。聲明別名是將復(fù)雜類型定義為更短名稱的類型的一種非常有效的方式。
你將注意怎么樣從結(jié)構(gòu)體中定義用“KeyType”和“ValueType”的參數(shù)類型中替換類型。上例的"KeyTypes"是數(shù)組類型的。當(dāng)然這是沒(méi)有這樣的類型的“KeyType”;當(dāng)在一般的實(shí)例化時(shí),將替代Swift像對(duì)OrderedDictionary的類型的一切類型通過(guò)。
就因?yàn)檫@樣,你將會(huì)注意到編譯錯(cuò)誤:
或許你會(huì)詫異怎么會(huì)這樣?請(qǐng)?jiān)儆^察下Dictionary的繼承者:
除了在KeyType之后的HashTable, 其他的都和OrderedDictionary的定義特別的相似。在分號(hào)后面為KeyType聲明的Hashable,一定符合Hashable的協(xié)議。這是因?yàn)樽值湫枰獮閔ash key實(shí)現(xiàn)。
用這種方式約束泛型參數(shù)是非常常見(jiàn)的。例如,你想要依據(jù)你的應(yīng)用使用參數(shù)做什么,來(lái)約束值的類型以,確保相等性、可打印性協(xié)議。
打開(kāi)OrderedDictionary.Swift,用下例來(lái)取代你對(duì)結(jié)構(gòu)體的定義:
這樣為OrderedDictionary聲明KeyType,必須符合Hashable。這就意味著,無(wú)論KeyType變成什么類型,都可以接受為沒(méi)有聲明的字典的KEY。
這樣,文件再次編譯,將不會(huì)報(bào)錯(cuò)!
Keys, Values 和所有的這些趣事
如果不能為字典添加值,那么字典有什么作用了?打開(kāi)OrderedDictionary.swift,在你的結(jié)構(gòu)體定義中添加以下函數(shù):
var adjustedIndex = index
// 2
let existingValue = self.dictionary[key]
if existingValue != nil {
// 3
let existingIndex = find(self.array, key)! // 4
if existingIndex < index {
adjustedIndex--
}
self.array.removeAtIndex(existingIndex)
} // 5
self.array.insert(key, atIndex:adjustedIndex)
self.dictionary[key] = value
// 6
return existingValue}
下面介紹一些新的特性。讓我們一步一步來(lái)介紹:
- 插入一個(gè)新對(duì)象的方法,insert(_:forKey:atIndex),需要三個(gè)參數(shù):一個(gè)特別的key的值,插入一對(duì)key-value的索引。這是你之前沒(méi)有注意到的一個(gè)關(guān)鍵字:改變。
- 結(jié)構(gòu)體的設(shè)計(jì)是默認(rèn)不變的,這意味著通常你在實(shí)例化的方法中,不能改變結(jié)構(gòu)的成員變量。這十分有限,你能添加改變的關(guān)鍵字,并告訴編譯器這個(gè)方式在結(jié)構(gòu)體中是允許改變的。這將幫助編譯器做出決定什么時(shí)候復(fù)制結(jié)構(gòu)體(他們是寫(xiě)時(shí)復(fù)制的),也有助于API的編檔。
- 你為字典的索引器輸入一個(gè)如果已經(jīng)存在,那么返回已存在的值的key,這個(gè)插入方法模擬字典更新值相同的行為,因此為這個(gè)值保持已經(jīng)存在的值。
- 如果這有一個(gè)已經(jīng)存在的值,只有這樣函數(shù)才能為這個(gè)值在數(shù)組里找出索引。
- 如果這個(gè)已經(jīng)存在的key在插入索引的之前,這時(shí)你需要調(diào)整插入的索引,因?yàn)槟阈枰瞥呀?jīng)存在的key。
- 你將適當(dāng)?shù)馗聰?shù)組和字典。
- 最后,你返回已存在的值,當(dāng)這或許沒(méi)有已存在的值,這個(gè)函數(shù)返回一個(gè)可選的值!
現(xiàn)在你可以為字典添加移除值?
像下列對(duì)OrderedDictionary結(jié)構(gòu)體的定義的函數(shù):
// 2
precondition(index < self.array.count, "Index out-of-bounds") // 3
let key = self.array.removeAtIndex(index) // 4
let value = self.dictionary.removeValueForKey(key)! // 5
return (key, value)}
現(xiàn)在再讓我們一步一步分析:
1.這是改變結(jié)構(gòu)體狀態(tài)的函數(shù),removeAtIndex的名稱需要和數(shù)組的方法匹配。恰當(dāng)?shù)臅r(shí)候,考慮使用鏡像系統(tǒng)庫(kù)中API是不錯(cuò)的選擇。這樣幫助開(kāi)發(fā)者在他們的工作平臺(tái)里,非常容易地使用你的API。
2.首先,你需要檢查索引,觀察他們是否是在大量的數(shù)組里。嘗試著從未聲明的數(shù)組中移除越位的元素,將會(huì)導(dǎo)致超時(shí)錯(cuò)誤,所有在這時(shí)檢查將會(huì)更早符合這樣的情況。你或許在Objective-C中使用斷言函數(shù);在Swift中斷言也是可使用的。但是前提是在釋放的工程中是活動(dòng)的,否則你運(yùn)行的應(yīng)用的將會(huì)終止。
3.接著,當(dāng)同時(shí)從數(shù)組中移除值時(shí),你在給定的索引中數(shù)組中獲得值。
4.然后,你從字典中為這個(gè)key移除的值,同時(shí)也會(huì)返回這個(gè)值。或許在給出的key中,字典也沒(méi)有相應(yīng)的值,所以removeValueForKey返回一個(gè)可選的。這種情況下,你知道字典將會(huì)為給出的key,包含一個(gè)值,因?yàn)檫@是唯一的自己給字典添加值的方法--insert(_:forKey:atIndex:),這時(shí)你可以選擇使用“!”,表明這將會(huì)有正義感值。
5.最后,你在一個(gè)元組返回的key和value。數(shù)組的removeAtIndex和字典的removeValueForKey是一樣的返回已存在的值功能。
值的讀取跟寫(xiě)入
把值寫(xiě)入字典(dictionary)是沒(méi)問(wèn)題了, 可是這樣還不夠! 你還需要實(shí)現(xiàn)一個(gè)方法(method) 從字典中讀出相應(yīng)的值.
打開(kāi) OrderedDictionary.swift 文件, 然后把下列代碼添加到結(jié)構(gòu)定義(struct definition)當(dāng)中, , 就放在 thearrayanddictionaryvariable 聲明的下面:
return self.array.count}
這個(gè)常用的屬性, 用來(lái)算出字典里面有幾條記錄. 只要返回?cái)?shù)組的 count 屬性的中值就可以了!
接下來(lái), 就是如何訪問(wèn)(Access)字典中的記錄了(Element). 我們可以通過(guò)下標(biāo)(Subscript)來(lái)訪問(wèn), 代碼如下:
下標(biāo)的語(yǔ)法我們會(huì)用了, 但是如果是我們自己定義的類那該怎么用呢? 好在 Swift 支持在自定義類里頭添加這項(xiàng)功能. 而且實(shí)現(xiàn)起來(lái)也不復(fù)雜.
把下列代碼添加到結(jié)構(gòu)定義的底部:
subscript(key: KeyType) -> ValueType? {
// 2(a)
get {
// 3
return self.dictionary[key]
}
// 2(b)
set {
// 4
if let index = find(self.array, key) {
} else {
self.array.append(key)
} // 5
self.dictionary[key] = newValue
}
}
我們?cè)敿?xì)解釋下這段代碼:
- 上面代碼注釋中標(biāo)有 1 的那一段:跟 func 和 var 類似, subscript 也是個(gè)關(guān)鍵字, 通過(guò)它定義下標(biāo). 參數(shù) key 是出現(xiàn)在中括號(hào)中的那個(gè)對(duì)象.
- 注釋中標(biāo)有 2 的那一段: 下標(biāo)由 setter 跟 getter 兩部分組成. 本例同時(shí)定義了 setter (代碼中的 set ) 跟 getter (代碼中的 get ). 當(dāng)然, 不是每個(gè)下標(biāo)都要同時(shí)定義 setter 跟 getter.
- 注釋中標(biāo)有 3 的那一段: getter 比較簡(jiǎn)單, 只要通過(guò)參數(shù) key, 在字典中找到相應(yīng)的值即可. 字典返回的是可選值(optinal), 如果 key 不存在, 該值為 nil.
- 注釋中標(biāo)有 4 的那一段: setter 就復(fù)雜些. 首先要檢測(cè)這個(gè) key 在有序字典里面是不是已經(jīng)存在. 如果不存在, 則把 key 添加到數(shù)組中. 由于我們需要把 key 添加到數(shù)組的尾部, 所以這里調(diào)用的是 append 方法.
- 注釋中標(biāo)有 5 的那一段: 把值添加到字典中. 這里用隱性命名的變量 newValue 獲取傳遞過(guò)來(lái)的值.
就像用 Swift 自帶的字典類的下標(biāo)那樣去, 你可以通過(guò) key 來(lái)查找某個(gè)值. 可是如果我們需要像訪問(wèn)數(shù)組那樣, 用下標(biāo)索引(index)訪問(wèn)某個(gè)值, 該怎么辦呢? 既然是有序字典, 沒(méi)有道理不能通過(guò)下標(biāo)索引一個(gè)一個(gè)的按順序訪問(wèn).
結(jié)構(gòu)體跟類可以定義多個(gè)參數(shù)類型不同的下標(biāo)(subscript). 把下列代碼添加到結(jié)構(gòu)定義的底部:
// 1
get {
// 2
precondition(index < self.array.count,
"Index out-of-bounds") // 3
let key = self.array[index] // 4
let value = self.dictionary[key]! // 5
return (key, value)
}}
這段代碼跟前面那段類似, 不同的是參數(shù)類型變成了 Int. 因?yàn)槲覀儸F(xiàn)在要實(shí)的功能是現(xiàn)像數(shù)組那樣, 使用下標(biāo)索引訪問(wèn)有序字典. 不過(guò)這次返回的是由 key 跟 value 組成的一個(gè)元組(tuple). 因?yàn)橛行蜃值渚褪怯蛇@樣一個(gè)一個(gè)的元組構(gòu)成的.
下面具體解釋下這段代碼:
- 這個(gè)下標(biāo)只定義了 getter. 當(dāng)然你也可以把 setter 加上. 不過(guò)要注意先檢查 index 會(huì)不會(huì)越界.
- index 的值不能超出數(shù)組的界限, 也就是字典元組的個(gè)數(shù). 我們可以利用 precondition 提示開(kāi)發(fā)人員, 程序出現(xiàn)越界訪問(wèn).
- 用 index 從數(shù)組中讀出 key.
- 再用 key 從字典中讀取 value. 需要注意的是, 由于數(shù)組中的每一個(gè) key 跟字典的 value 是一一對(duì)應(yīng)的, 所以這里使用符號(hào) ! (unwrapped) 對(duì)讀出來(lái)的 value 拆包.
- 最后, 返回一個(gè)包含 key 和 value 的元組.
挑戰(zhàn): 為上面那個(gè)下標(biāo)實(shí)現(xiàn) setter . 可以參考前面的例子.
提示 1
注意, newValue 是個(gè)包含 key 跟 value 的元組.
提示 2
下列代碼可以將值從元組中提取出來(lái):
也許你會(huì)好奇, 如果 KeyType 是 Int 型的, 會(huì)出現(xiàn)什么問(wèn)題? 使用泛型的好處是, 不管是什么類型, 只要能算出哈希值(hashable)的就行, 所以 Int 當(dāng)然也能用. 問(wèn)題是, 當(dāng) key 也是 Int 型的時(shí)候, 這倆個(gè)下標(biāo)該怎么區(qū)分呢?
這就需要我們給編譯器提供更多的類型信息. 讓它知道在什么時(shí)調(diào)用哪個(gè)下標(biāo). 比如我們定義的這兩個(gè)下標(biāo), 返回的類型不一樣. 如果你用 key-value 類型的元組給它賦值, 編譯器就會(huì)自動(dòng)調(diào)用那個(gè)數(shù)組式(array-style )的下標(biāo).
在項(xiàng)目中測(cè)試
讓我們?cè)趯?shí)際項(xiàng)目中,實(shí)驗(yàn)編譯推斷使用的下標(biāo)函數(shù),以及一般情況下,OrderedDictionary是怎么工作的。
通過(guò)點(diǎn)擊"文件"、"創(chuàng)建"、"文件",新建一個(gè)項(xiàng)目,依次選擇"IOS"、"Source"、"Playground",再點(diǎn)擊下一步。然后點(diǎn)擊創(chuàng)建。
你必須得這樣操作:復(fù)制和粘貼OrderedDictionary.swift整體到新建的項(xiàng)目中。因?yàn)椴荒茉趯?xiě)教程時(shí)你的應(yīng)用模型中”看見(jiàn)”代碼
注意:這有一個(gè)解決方法,可以取代復(fù)制、粘貼的方式。如果你需要將你應(yīng)用的代碼加入到一個(gè)框架中,你的項(xiàng)目將接受的你代碼,就像Corrine Krych指出的這樣。
現(xiàn)在,在你的項(xiàng)目底部加入下列的代碼:
+ " : "
+ dict.dictionary.description) var byIndex: (Int, String) = dict[0]println(byIndex) var byKey: String? = dict[2]println(byKey)
在側(cè)欄中(或者通過(guò)視圖/助理編輯/顯示助理編輯/),你將可以看到println()函數(shù)輸出的內(nèi)容:
在這個(gè)例子中,字典有一個(gè)整數(shù)型的key,所以編譯器會(huì)審查被使用分配決定使用哪個(gè)下標(biāo)變量的類型。如果被使用的下標(biāo)是一個(gè)(Int, String)的byIndex,編譯器會(huì)匹配期望的返回值類型,使用數(shù)組類型的索引的下標(biāo)。
如果你想從一個(gè) byIndex 或者 byKey的變量中,移除類型的定義。編譯器將會(huì)報(bào)錯(cuò),表明編譯器不知道使用哪一個(gè)下標(biāo)。
小貼士:由于編譯器是按照類型推理來(lái)工作的,所以需要明確地表示出類型。當(dāng)存在多個(gè)有相同的爭(zhēng)議的返回值類型的函數(shù)時(shí),調(diào)用者需要具體化。需要注意:Swift中函數(shù),可以“建-破”改變。
通過(guò)在項(xiàng)目中,對(duì)有順序的字典的實(shí)驗(yàn)中,你可以發(fā)現(xiàn)他的工作原理。在重返app之前,嘗試從中添加、移除、以及改變key和value的類型。現(xiàn)在,你可以在你的順序字典中讀、寫(xiě)操作!但要小心你的數(shù)據(jù)結(jié)構(gòu)。現(xiàn)在你可以通過(guò)app來(lái)感受其中的樂(lè)趣了!
添加圖片查找
現(xiàn)在是時(shí)候讓你回過(guò)頭來(lái)注意手中的app了。打開(kāi) MasterViewController.swift。在兩個(gè)@IBOutlets 的下面,添加下列變量的定義:
你或許在困惑,為什么Flickr.Photo的類型中有個(gè)句號(hào)。那是因?yàn)镻hoto是在Flickr類的內(nèi)部定義的類。在Swift中,這樣的層次結(jié)構(gòu)是非常有利的。它將有助于類的名稱簡(jiǎn)短化。在Flickr的內(nèi)部,你可以單獨(dú)使用Photo類,因?yàn)樯舷挛年P(guān)系告訴了編譯器這是什么。這是順序化字典查詢用戶訂閱的Flickr的功能。真像你看到的,包含查詢的字符串,和Flickr.Photo數(shù)組,或是從Flickr API 中返回的照片。注意,你在尖括號(hào)里給出的key和value,將成為在具體實(shí)現(xiàn)中KeyType和ValueType的參數(shù)類型。
接下來(lái),找到表格視圖數(shù)據(jù)源的tableView(_:numberOfRowsInSection:)方法,然后把它改為如下所示:
numberOfRowsInSection section: Int) -> Int{
return self.searches.count}
這個(gè)方法使用有序字典來(lái)告訴表格視圖有多少行。接著,找到表格視圖數(shù)據(jù)源的tableView(_:cellForRowAtIndexPath:)方法并把它改為如下所示:
func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell{
// 1
let cell =
tableView.dequeueReusableCellWithIdentifier("Cell",
forIndexPath: indexPath) as UITableViewCell // 2
let (term, photos) = self.searches[indexPath.row] // 3
if let textLabel = cell.textLabel {
textLabel.text = "/(term) (/(photos.count))"
}
return cell}
這是你在這個(gè)方法中所做的:
1. 首先,從UITableView中挪出一個(gè)單元格。你需要把它直接轉(zhuǎn)換為UITableViewCell,因?yàn)閐equeueReusableCellWithIdentifier仍舊返回AnyObject(id in Objective-C),而不是UITableViewCell。或許在將來(lái),蘋(píng)果公司會(huì)利用泛型重寫(xiě)這部分API。
2. 接著,用你給的下標(biāo)索引從指定的行獲取Key和Value,
3. 最后,適當(dāng)?shù)卦O(shè)置單元格的文本標(biāo)簽并且返回當(dāng)前單元格。
現(xiàn)在讓我們嘗嘗鮮。找到UISearchBarDelegate 的拓展,就像下列的代碼一樣,改變單例方法。
func searchBarSearchButtonClicked(searchBar: UISearchBar!) {
// 1
searchBar.resignFirstResponder() // 2
let searchTerm = searchBar.text
Flickr.search(searchTerm) {
switch ($0) {
case .Error:
// 3
break case .Results(let results):
// 4
self.searches.insert(results,
forKey: searchTerm,
atIndex: 0) // 5
self.tableView.reloadData()
}
}}
當(dāng)用戶點(diǎn)擊查詢按鈕時(shí),這個(gè)方法將會(huì)被調(diào)用。下列就是在這個(gè)方法中,你正做的事:
1.你第一反應(yīng)是放棄使用查詢框以及鍵盤(pán)。
2.然后,你又會(huì)使用搜索框查詢你輸入的文字,為查詢的文字而使用Flickr類來(lái)尋找。Flickr的查詢方法是:查詢術(shù)語(yǔ),關(guān)閉執(zhí)行查詢成功或者失敗。通過(guò)參數(shù)關(guān)閉:要不是錯(cuò)誤,那就是結(jié)果。
3.在錯(cuò)誤的情況下,不會(huì)發(fā)生任何事。但是你可以通過(guò)使用警報(bào)來(lái)提示錯(cuò)誤,但是現(xiàn)在我們可以簡(jiǎn)化這樣的操作。代碼需要在這時(shí)暫停會(huì)兒,告訴Swfit編譯器你的錯(cuò)誤沒(méi)有任何反應(yīng)的動(dòng)機(jī)。
4.如果查詢有效,將會(huì)在查詢結(jié)果中顯示相關(guān)的值。你將查詢的術(shù)語(yǔ)作為key加入到順序字典中。如果已經(jīng)在字典中存在了,將會(huì)把他放入到list的頂部,然后用最后的結(jié)果更新整個(gè)字典。
5.最終,由于你有新的數(shù)據(jù),將再次加載table view。
哇!你的app將可以用于查詢圖片了!
構(gòu)建并運(yùn)行app,做幾次查詢。你將會(huì)看到下面這樣的一些東西:
現(xiàn)在再用與之前搜索詞不同的另外一個(gè)進(jìn)行查詢. 你將會(huì)看到它跳到了頂部:
選擇一個(gè)查詢結(jié)果點(diǎn)擊去,你會(huì)發(fā)現(xiàn)它并沒(méi)有顯示照片。現(xiàn)在是時(shí)候修復(fù)這個(gè)問(wèn)題了!
給我看照片!
打開(kāi)MasterViewController.swift 并找到 prepareForSegue. 將它修改成下面這樣:
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?){
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow()
{
let (_, photos) = self.searches[indexPath.row]
(segue.destinationViewController as DetailViewController).photos = photos }
}}
這是用了同創(chuàng)建單元項(xiàng)時(shí)訪問(wèn)被排好序的查詢結(jié)果字典一樣的方法. 盡管沒(méi)有使用關(guān)鍵詞(檢索詞), 你也可以用下劃線來(lái)顯示出元組的這個(gè)部分不需要被綁定到一個(gè)本地變量.
構(gòu)建并運(yùn)行app,做一次查詢?nèi)缓簏c(diǎn)進(jìn)去。你會(huì)看到像下面這樣的東西:
你好,小貓貓! 你有沒(méi)有想要發(fā)出快樂(lè)的呼聲呢? :]