Swift35/90Days - iOS 中的設計模式 (Swift 版本) 01

Swift90Days - iOS 中的設計模式 (Swift 版本) 01

更新聲明

翻譯自 Introducing iOS Design Patterns in Swift – Part 1/2 ,本教程 objc 版本的做者是 Eli Ganem ,由 Vincent Ngo 更新爲 Swift 版本。html

媽蛋這文章太長了,再加上天天加班到10點多基本沒時間,拖了10幾天才完成。。。ios

iOS 設計模式

說到設計模式,相信你們都不陌生,可是又有多少人知道它背後的真正含義?絕大多數程序員都知道設計模式十分重要,不過關於這個話題的文章卻不是不少,開發者們在開發的時候有時也不太在乎設計模式方面的內容。git

設計模式針對軟件設計中的常見問題,提供了一些可複用的解決方案,開發者能夠經過這些模板寫出易於理解且可以複用的代碼。正確的使用設計模式能夠下降代碼之間的耦合度,從而很輕鬆的修改或者替換之前的代碼。程序員

若是你對設計模式還很陌生,那麼告訴你一個好消息!在 iOS 的開發過程當中,其實你不知不覺已經用了不少設計模式。這得益於 Cocoa 提供的框架和一些良好的編程習慣。接下來的這篇教程將會帶你一塊兒飛,去領略設計模式的魅力。github

整個教程分爲兩篇文章,經過整個系列的學習,咱們將會完成一個完整的應用,展現音樂專輯和專輯的相關信息。編程

經過這個應用,咱們會接觸一些 Cocoa 中常見的設計模式:swift

  • 建立型 (Creational):單例模式 (Singleton)
  • 結構型 (Structural):MVC、裝飾者模式 (Decorator)、適配器模式 (Adapter)、外觀模式 (Facade)
  • 行爲型 (Behavioral):觀察者模式 (Observer)、備忘錄模式 (Memento)

嘿嘿嘿別愁眉苦臉的嘛,這篇文章不是什麼長篇大論的理論知識,你會在開發應用的過程當中慢慢學會這些設計模式。設計模式

先來預覽一下最終的結果:api

看起來仍是不錯的,開始學習接下來的內容吧。勇敢的少年們,快來創造奇蹟!數組

開始

下載初始項目並解壓,在 Xcode 中打開 BlueLibrarySwift.xcodeproj 項目文件。

項目中有三個地方須要注意一下:

  1. ViewController 有兩個 IBOutlet ,分別鏈接到了 UITableViewUIToolBar 上。

  2. 在 StoryBoard 上有三個組件設置了約束。最上面的是專輯的封面,封面下面是列舉了相關專輯的列表,最下面是有兩個按鈕的工具欄,一個用來撤銷操做,另外一個用來刪除你選中的專輯。 StoryBoard 看起來是這個樣子的:

  1. 一個簡單的 HTTP 客戶端類 (HTTPClient) ,裏面尚未什麼內容,須要你去完善。

注意:其實當你建立一個新的 Xcode 的項目的時候,你的代碼裏就已經有不少設計模式的影子了: MVC、委託、代理、單例 - 真是衆裏尋他千百度,得來全不費功夫。

在學習第一個設計模式以前,你須要建立兩個類,用來存儲和展現專輯數據。

建立一個新的類,繼承 NSObject 名爲 Album ,記得選擇 Swift 做爲編程語言而後點擊下一步。

打開 Album.swift 而後添加以下定義:

var title : String!
var artist : String!
var genre : String!
var coverUrl : String!
var year : String!

這裏建立了五個屬性,分別對應專輯的標題、做者、流派、封面地址和出版年份。

接下來咱們添加一個初始化方法:

init(title: String, artist: String, genre: String, coverUrl: String, year: String) {
  super.init()

  self.title = title
  self.artist = artist
  self.genre = genre
  self.coverUrl = coverUrl
  self.year = year
}

這樣咱們就能夠愉快的初始化了。

而後再加上下面這個方法:

func description() -> String {
  return "title: \(title)" +
   "artist: \(artist)" +
   "genre: \(genre)" +
   "coverUrl: \(coverUrl)" +
   "year: \(year)"
}

這是專輯對象的描述方法,詳細的打印了 Album 的全部屬性值,方便咱們查看變量各個屬性的值。

接下來,再建立一個繼承自 UIView 的視圖類 AlbumView.swift

在新建的類中添加兩個屬性:

private let coverImage: UIImageView! 
private let indicator: UIActivityIndicatorView!

coverImage 表明了封面的圖片,indicator 則是在加載過程當中顯示的等待指示器。

這兩個屬性都是私有屬性,由於除了 AlbumView 以外,其餘類沒有必要知道他倆的存在。在寫一些框架或者類庫的時候,這種規範十分重要,能夠避免一些誤操做。

接下來給這個類添加初始化化方法:

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

init(frame: CGRect, albumCover: String) {
    super.init(frame: frame)
    backgroundColor = UIColor.blackColor()
    coverImage = UIImageView(frame: CGRectMake(5, 5, frame.size.width - 10, frame.size.height - 10))
    addSubview(coverImage)
    indicator = UIActivityIndicatorView()
    indicator.center = center
    indicator.activityIndicatorViewStyle = .WhiteLarge
    indicator.startAnimating()
    addSubview(indicator)
}

由於 UIView 聽從 NSCoding 協議,因此咱們須要 NSCoder 的初始化方法。不過目前咱們沒有 encodedecode 的必要,因此就把它放在那裏就行,調用父類方法初始化便可。

在真正的初始化方法裏,咱們設置了一些初始化的默認值。好比設置背景顏色默認爲黑色,建立 ImageView 並設置了 margin 值,添加了一個加載指示器。

最終咱們再加上以下方法:

func highlightAlbum(#didHighlightView: Bool) {
    if didHighlightView == true {
        backgroundColor = UIColor.whiteColor()
    } else {
        backgroundColor = UIColor.blackColor()
    }
}

這會切換專輯的背景顏色,若是高亮就是白色,不然就是黑色。

在繼續下面的內容以前, Command + B 試一下確保沒有什麼問題,一切正常?那就開始第一個設計模式的學習啦!:]

MVC - 設計模式之王

Model-View-Controller (縮寫 MVC ) 是 Cocoa 框架的一部分,而且毋庸置疑是最經常使用的設計模式之一。它能夠幫你把對象根據職責進行劃分和歸類。

做爲劃分依據的三個基本職責是:

  • 模型層 (Model) :存儲數據而且定義如何操做這些數據。在咱們的例子中,就是 Album 類。
  • 視圖層 (View) :負責模型層的可視化展現,而且負責用戶的交互,通常來講都是繼承自 UIView 這個基類。在咱們的項目中就是 AlbumView 這個類。
  • 控制器 (Controller) :控制器是整個系統的掌控者,它鏈接了模型層和數據層,而且把數據在視圖層展現出來,監聽各類事件,負責數據的各類操做。不妨猜猜在咱們的項目中哪一個是控制器?啊哈猜對了:ViewController 這個類就是。

若是你的項目遵循 MVC 的設計模式,那麼各類對象要不是 Model ,要不是 View ,要不就是 Controller。固然在實際的開發中也能夠靈活變化,好比結合具體業務使用 MVVM 結構給 ViewController 瘦瘦身,也是能夠的。

三者之間的關係以下:

模型層通知控制器層任何數據的變化,而後控制器層會刷新視圖層中的數據。視圖層能夠通知控制器層用戶的交互事件,而後控制器會處理各類事件以及刷新數據。

你可能會感受奇怪:爲何要把這三個東西分開來,而不能揉在一個類裏呢?那樣彷佛更簡單一點嘛。

Naive.

之因此這樣作,是爲了將代碼更好的分離和重用。理想狀態下,視圖層應當和模型層徹底分離。若是視圖層不依賴任何模型層的具體實現,那麼就能夠很容易的被其餘模型複用,用來展現不一樣的數據。

舉個例子,好比在將來咱們須要添加電影或者什麼書籍,咱們依舊可使用 AlbumView 這個類做爲展現。更久遠點來講,在之後若是你建立了一個新的項目而且須要用到和專輯相關的內容,你能夠直接複用 Album 類由於它並不依賴於任何視圖模塊。這就是 MVC 的強大之處,三大元素,各司其職,減小依賴。

如何使用 MVC 模式

首先,你須要肯定你的項目中的每一個類都是三大基本類型中的一種:控制器、模型、視圖。不要在一個類裏糅合多個角色。目前咱們建立了 Album 類和 AlbumView 類是符合要求的,作得很好。

而後,爲了確保你遵循這種模式,你最好建立三個項目分組來存放代碼,分別是 Model、View、Controller,保持每一個類型的文件分別獨立。

接下來把 Album.swift 拖到 Model 分組,把 AlbumView.swift 拖到 View 分組,而後把 ViewController.swift 拖到 Controller 分組中。

如今你的項目應該是這個樣子:

如今你的項目已經有點樣子了,再也不是各個文件顛沛流離居無定所了。顯然你還會有其餘分組和類,可是應用的核心就在這三個類裏。

如今你的內容已經組織好了,接下來要作的就是獲取專輯的數據。你將會建立一個 API 類來管理數據 - 這裏咱們會用到下一個設計模式:單例模式。

單例模式

單例模式確保每一個指定的類只存在一個實例對象,而且能夠全局訪問那個實例。通常狀況下會使用延時加載的策略,只在第一次須要使用的時候初始化。

注意:在 iOS 中單例模式很常見,NSUserDefaults.standardUserDefaults()UIApplication.sharedApplication()UIScreen.mainScreen()NSFileManager.defaultManager() 這些都是單例模式。

你可能會疑惑了:若是多於一個實例又會怎麼樣呢?代碼和內存還沒精貴到這個地步吧?

某些場景下,保持實例對象僅有一份是頗有意義的。舉個例子,你的應用實例 (UIApplication),應該只有一個吧,顯然是指你的當前應用。還有一個例子:設備的屏幕 (UIScreen) 實例也是這樣,因此對於這些類的狀況,你只想要一個實例對象。

單例模式的應用還有另外一種狀況:你須要一個全局類來處理配置文件。咱們很容易經過單例模式實現線程安全的實例訪問,而若是有多個類能夠同時訪問配置文件,那可就複雜多了。

如何使用單例模式

能夠看下這個圖:

這是一個日誌類,有一個屬性 (是一個單例對象) 和兩個方法 (sharedInstance()init())。

第一次調用 sharedInstance() 的時候,instance 屬性尚未初始化。因此咱們要建立一個新實例而且返回。

下一次你再調用 sharedInstance() 的時候,instance 已經初始化完成,直接返回便可。這個邏輯確保了這個類只存在一個實例對象。

接下來咱們繼續完善單例模式,經過這個類來管理專輯數據。

注意到在咱們前面的截圖裏,分組中有個 API 分組,這裏能夠放那些提供後臺服務的類。在這個分組中建立一個新的文件 LibraryAPI.swift ,繼承自 NSObject 類。

LibraryAPI 裏添加下面這段代碼:

//1
class var sharedInstance: LibraryAPI {
    //2
    struct Singleton {
        //3
        static let instance = LibraryAPI()
    }
    //4
    return Singleton.instance
}

在這幾行代碼裏,作了以下工做:

  • 建立一個計算類型的類變量,這個類變量,就像是 objc 中的靜態方法同樣,能夠直接經過類訪問而不用實例對象。具體可參見蘋果官方文檔的 屬性 這一章。

  • 在類變量裏嵌套一個 Singleton 結構體。

  • Singleton 封裝了一個靜態的常量,經過 static 定義意味着這個屬性只存在一個,注意 Swift 中 static 的變量是延時加載的,意味着 Instance 直到須要的時候纔會被建立。同時再注意一下,由於它是一個常量,因此一旦建立以後不會再建立第二次。這些就是單例模式的核心所在:一旦初始化完成,當前類存在一個實例對象,初始化方法就不會再被調用。

  • 返回計算後的屬性值。

注意:更多的單例模式實例能夠看看 Github 上的這個示例,列舉了單例模式的若干種實現方式。

你如今能夠將這個單例做爲專輯管理類的入口,接下來咱們繼續建立一個處理專輯數據持久化的類。

新建 PersistencyManager.swift 並添加以下代碼:

private var albums = [Album]()

在這裏咱們定義了一個私有屬性,用來存儲專輯數據。這是一個可變數組,因此你能夠很容易的增長或者刪除數據。

而後加上一些初始化的數據:

override init() {
  //Dummy list of albums
  let album1 = Album(title: "Best of Bowie",
         artist: "David Bowie",
         genre: "Pop",
         coverUrl: "http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png",
         year: "1992")

  let album2 = Album(title: "It's My Life",
         artist: "No Doubt",
         genre: "Pop",
         coverUrl: "http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png",
         year: "2003")

  let album3 = Album(title: "Nothing Like The Sun",
         artist: "Sting",
         genre: "Pop",
         coverUrl: "http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png",
         year: "1999")

  let album4 = Album(title: "Staring at the Sun",
         artist: "U2",
         genre: "Pop",
         coverUrl: "http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png",
         year: "2000")

  let album5 = Album(title: "American Pie",
         artist: "Madonna",
         genre: "Pop",
         coverUrl: "http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png",
         year: "2000")

  albums = [album1, album2, album3, album4, album5]
}

在這個初始化方法裏,咱們初始化了五張專輯。若是上面的專輯沒有你喜歡的,你能夠隨意替換成你的菜:]

而後添加以下方法:

func getAlbums() -> [Album] {
  return albums
}

func addAlbum(album: Album, index: Int) {
  if (albums.count >= index) { 
    albums.insert(album, atIndex: index)
  } else {
    albums.append(album)
  }
}

func deleteAlbumAtIndex(index: Int) {
  albums.removeAtIndex(index)
}

這些方法可讓你自由的訪問、添加、刪除專輯數據。

這時你能夠運行一下你的項目,確保編譯經過以便進行下一步操做。

此時你或許會感到好奇: PersistencyManager 好像不是單例啊?是的,它確實不是單例。不過不要緊,在接下來的外觀模式章節,你會看到 LibraryAPIPersistencyManager 之間的聯繫。

外觀模式

外觀模式在複雜的業務系統上提供了簡單的接口。若是直接把業務的全部接口直接暴露給使用者,使用者須要單獨面對這一大堆複雜的接口,學習成本很高,並且存在誤用的隱患。若是使用外觀模式,咱們只要暴露必要的 API 就能夠了。

下圖演示了外觀模式的基本概念:

API 的使用者徹底不知道這內部的業務邏輯有多麼複雜。當咱們有大量的類而且它們使用起來很複雜並且也很難理解的時候,外觀模式是一個十分理想的選擇。

外觀模式把使用和背後的實現邏輯成功解耦,同時也下降了外部代碼對內部工做的依賴程度。若是底層的類發生了改變,外觀的接口並不須要作修改。

舉個例子,若是有一天你想換掉全部的後臺服務,你只須要修改 API 內部的代碼,外部調用 API 的代碼並不會有改動。

如何使用外觀模式

如今咱們用 PersistencyManager 來管理專輯數據,用 HTTPClient 來處理網絡請求,項目中的其餘類不該該知道這個邏輯。他們只須要知道 LibraryAPI 這個「外觀」就能夠了。

爲了實現外觀模式,應該只讓 LibraryAPI 持有 PersistencyManagerHTTPClient 的實例,而後 LibraryAPI 暴露一個簡單的接口給其餘類來訪問,這樣外部的訪問類不須要知道內部的業務具體是怎樣的,也不用知道你是經過 PersistencyManager 仍是 HTTPClient 獲取到數據的。

大體的設計是這樣的:

LibraryAPI 會暴露給其餘代碼訪問,可是 PersistencyManagerHTTPClient 則是不對外開放的。

打開 LibraryAPI.swift 而後添加以下代碼:

private let persistencyManager: PersistencyManager
private let httpClient: HTTPClient
private let isOnline: Bool

除了兩個實例變量以外,還有個 Bool 值: isOnline ,這個是用來標識當前是否爲聯網狀態的,若是是聯網狀態就會去網絡獲取數據。

咱們須要在 init 裏面初始化這些變量:

override init() {
  persistencyManager = PersistencyManager()
  httpClient = HTTPClient()
  isOnline = false

  super.init()
}

HTTPClient 並不會直接和真實的服務器交互,只是用來演示外觀模式的使用。因此 inOnline 這個值咱們一直設置爲 false

接下來在 LibraryAPI.swift 裏添加以下代碼:

func getAlbums() -> [Album] {
  return persistencyManager.getAlbums()
}

func addAlbum(album: Album, index: Int) {
  persistencyManager.addAlbum(album, index: index)
  if isOnline {
    httpClient.postRequest("/api/addAlbum", body: album.description())
  }
}

func deleteAlbum(index: Int) {
  persistencyManager.deleteAlbumAtIndex(index)
  if isOnline {
    httpClient.postRequest("/api/deleteAlbum", body: "\(index)")
  }
}

看一下 addAlbum(_:index:) 這個方法,先更新本地緩存,而後若是是聯網狀態還須要向服務器發送網絡請求。這即是外觀模式的強大之處:若是外部文件想要添加一個新的專輯,它不會也不用去了解內部的實現邏輯是怎麼樣的。

注意:當你設計外觀的時候,請務必牢記:使用者隨時可能直接訪問你的隱藏類。永遠不要假設使用者會遵循你當初的設計作事。

運行一下你的應用,你能夠看到兩個空的頁面和一個工具欄:最上面的視圖用來展現專輯封面,下面的視圖展現數據列表。

你須要在屏幕上展現專輯數據,這是就該用下一種設計模式了:裝飾者模式。

裝飾者模式

裝飾者模式能夠動態的給指定的類添加一些行爲和職責,而不用對原代碼進行任何修改。當你須要使用子類的時候,不妨考慮一下裝飾者模式,能夠在原始類上面封裝一層。

在 Swift 裏,有兩種方式實現裝飾者模式:擴展 (Extension) 和委託 (Delegation)。

擴展

擴展是一種十分強大的機制,可讓你在不用繼承的狀況下,給已存在的類、結構體或者枚舉類添加一些新的功能。最重要的一點是,你能夠在你沒有訪問權限的狀況下擴展已有類。這意味着你甚至能夠擴展 Cocoa 的類,好比 UIView 或者 UIImage

舉個例子,在編譯時新加的方法能夠像擴展類的正常方法同樣執行。這和裝飾器模式有點不一樣,由於擴展不會持有擴展類的對象。

如何使用擴展

想象一下這個場景,咱們須要在下面這個列表裏展現數據:

專輯標題從哪裏來? Album 自己是個 Model 對象,因此它不該該負責如何展現數據。你須要一些額外的代碼添加展現數據的邏輯,可是爲了保持 Model 的乾淨,咱們不該該直接修改代碼,由於這樣不符合單一職責原則。 Model 層最好就是負責純粹的數據結構,若是有數據的操做能夠放到擴展中完成。

接下來咱們會建立一個擴展,擴展示有的 Album 類,在擴展裏定義了新的方法,返回更適合 UITableView 展現用的數據結構。

數據的結構大概是這樣:

新建一個 Swift 文件:AlbumExtensions ,在裏面添加以下擴展:

extension Album {
  func ae_tableRepresentation() -> (titles:[String], values:[String]) {
    return (["Artist", "Album", "Genre", "Year"], [artist, title, genre, year])
  }
}

在方法的前面有個 ae_ 前綴,是 AlbumExtension 的縮寫,這樣有利於和類的原有方法進行區分,避免使用的時候產生衝突。如今不少還在維護中的第三方庫都已經改爲了這個風格。

注意:類是能夠重寫父類方法的,可是在擴展裏不能夠。擴展裏的方法和屬性不能和原始類裏的方法和屬性衝突。

思考一下這個設計模式的強大之處:

  • 咱們能夠直接在擴展裏使用 Album 裏的屬性。
  • 咱們給 Album 類添加了內容可是並無繼承它,事實上,使用繼承來擴展業務也能夠實現同樣的功能。
  • 這個簡單的擴展讓咱們能夠更好地把 Album 的數據展現在 UITableView 裏,並且不用修改源碼。

委託

裝飾者模式的另外一種實現方案是委託。在這種機制下,一個對象能夠和另外一個對象相關聯。好比你在用 UITableView ,你必須實現 tableView(_:numberOfRowsInSection:) 這個委託方法。

你不該該期望 UITableView 知道你有多少數據,這是個應用層該解決的問題。因此,數據相關的計算應該經過 UITableView 的委託來解決。這樣可讓 UITableView 和數據層分別獨立。視圖層就負責顯示數據,你遞過來什麼我就顯示什麼。

下面這張圖很好的解釋了 UITableView 的工做過程:

UITableView 的工做僅僅是展現數據,可是最終它須要知道本身要展現那些數據,這時就能夠向它的委託詢問。在 objc 的委託模式裏,一個類能夠經過協議來聲明可選或者必須的方法。

看起來彷佛繼承而後重寫必須的方法來的更簡單一點。可是考慮一下這個問題:繼承的結果一定是一個獨立的類,若是你想讓某個對象成爲多個對象的委託,那麼子類這招就行不通了。

注意:委託模式十分重要,蘋果在 UIKit 中大量使用了該模式,基本上隨處可見。

如何使用委託模式

打開 ViewController.swift 文件,添加以下私有變量:

private var allAlbums = [Album]()
private var currentAlbumData : (titles:[String], values:[String])?
private var currentAlbumIndex = 0

viewDidLoad 裏面加入以下內容:

override func viewDidLoad() {
    super.viewDidLoad()
    //1
    self.navigationController?.navigationBar.translucent = false
    currentAlbumIndex = 0

    //2
    allAlbums = LibraryAPI.sharedInstance.getAlbums()

    // 3
    // the uitableview that presents the album data
    dataTable.delegate = self
    dataTable.dataSource = self
    dataTable.backgroundView = nil
    view.addSubview(dataTable!)       
}

對上面三個部分進行拆解:

  1. 關閉導航欄的透明效果

  2. 經過 API 獲取全部的專輯數據,記住,咱們使用外觀模式以後,應該從 LibraryAPI 獲取數據,而不是 PersistencyManager

  3. 你能夠在這裏設置你的 UITablweView ,在這裏聲明瞭 UITableViewdelegate 是當前的 ViewController 。事實上你用了 XIB 或者 StoryBoard ,能夠直接在可視化的頁面裏拖拽完成。

接下來添加一個新的方法用來更方便的獲取數據:

func showDataForAlbum(albumIndex: Int) {
    // defensive code: make sure the requested index is lower than the amount of albums
    if (albumIndex < allAlbums.count && albumIndex > -1) {
        //fetch the album
        let album = allAlbums[albumIndex]
        // save the albums data to present it later in the tableview
        currentAlbumData = album.ae_tableRepresentation()
    } else {
        currentAlbumData = nil
    }
    // we have the data we need, let's refresh our tableview
    dataTable!.reloadData()
}

showDataForAlbum() 這個方法獲取最新的專輯數據,當你想要展現新數據的時候,你須要調用 reloadData() 這個方法,這樣 UITableView 就會向委託請求數據,好比有多少個 section 有多少個 row 之類的。

viewDidLoad 裏面調用上面的方法:

self.showDataForAlbum(currentAlbumIndex)

這樣應用一啓動就會去加載當前的專輯數據。由於 currentAlbumIndex 的默認值是 0 ,因此一開始會默認顯示第一章專輯的信息。

接下來咱們該去完善 DataSource 的協議方法了。你能夠直接把委託方法寫在類裏面,固然若是你想讓你的代碼看起來更整潔一點,則能夠放在擴展裏。

在文件底部添加以下方法,注意必定要放在類定義的大括號外面,由於這兩個傢伙不是類定義的一部分,它們是擴展:

extension ViewController: UITableViewDataSource {
}

extension ViewController: UITableViewDelegate {
}

上面就是實現委託的方法 - 你能夠把協議想象成是與委託之間的約定,只要你實現了約定的方法,就算是實現了委託。在咱們的代碼中, ViewController 須要遵照 UITableViewDataSourceUITableViewDelegate 的協議。這樣 UITableView 才能確保必要的委託方法都已經實現了。

UITableViewDataSource 對應的那個擴展里加上以下方法:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  if let albumData = currentAlbumData {
    return albumData.titles.count
  } else {
    return 0
  }
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
  if let albumData = currentAlbumData {
    cell.textLabel?.text = albumData.titles[indexPath.row]
      if let detailTextLabel = cell.detailTextLabel {
        detailTextLabel.text = albumData.values[indexPath.row]
      }
  }
  return cell
}

tableView(_:numberOfRowsInSection:) 返回須要展現的行數,和存儲的數據中的 title 的數目相同。

tableView(_:cellForRowAtIndexPath:) 建立而且返回了一個單元格,上面有標題和對應的值。

注意:你能夠把這些方法直接加在類聲明裏面,也能夠放在擴展裏,編譯器不會去管數據源到底在哪裏,只要能找到對應的方法就能夠了。而咱們之因此這樣作,是爲了方便其餘人閱讀。

此時再構建項目,你能夠看到以下內容:

是的,顯示成功啦!目前的項目源碼在這裏:BlueLibrarySwift-Part1,若是遇到什麼問題你能夠下載下來對比一下。

下一張咱們會繼續設計模式的內容,敬請期待!


原文連接:

-Introducing iOS Design Patterns in Swift – Part 1/2

相關文章
相關標籤/搜索