原文地址: RAYWENDERLICH
說明:英文水平有限,主要是為了鞏固學(xué)到的知識,也能幫別人快速上手,節(jié)約時間,有任何破綻,尤其是技術(shù)上的,請您一定要告訴我。
(旁白:其實這張圖幾乎說明了所有問題,設(shè)置好
constraints
,其它都不是問題。補充一下:這篇教程實際上就是 auto layout教程。)
你應(yīng)該在使用 table view cells 的時候經(jīng)常會寫很多代碼,來手動計算label,image view,text field 和 cell 相關(guān)的每一個控件的高。
坦白講,這種方法很容易出錯且容易讓人迷糊看不懂。
在這篇教程里,你將學(xué)會怎樣創(chuàng)建自定義 cell 并且根據(jù)內(nèi)容動態(tài)調(diào)整 cell 的高度,你可能會想,"這得需要多少工作量...!"
Nope! * (旁白:you are right ~*)
你很幸運,Apple 可以讓你在 iOS8 里很容易的做到這些。你將從寫適配代碼中解脫出來。但是還是要實現(xiàn) table view 的數(shù)據(jù)源和代理方法。
讓我們開始
iOS6出來幾天之后,Apple 介紹了一個超贊的技術(shù):auto layout。程序猿們開始慶祝,街頭聚會,寫贊歌...(旁白:要不要這么夸張啊)
好吧,也許有些問題,但它畢竟是一次大飛躍(旁白:是這個意思吧,哈哈)
在給了很多開發(fā)者希望的同時,auto layout 還是很難用的。尤其是手寫 auto layout 代碼。Interface Builder 在設(shè)置 constraints 時也并不理想。
很快到現(xiàn)在,伴隨著對 Interface Builder 的所有改進和 iOS8 的到來,我們現(xiàn)在可以很容易的動態(tài)設(shè)置 table view cells 的高度了。
你需要不得不做的事情有:
1,在創(chuàng)建 table view cells 時使用 auto layout。
2,設(shè)置 table view 的** rowHeight 等于 UITableViewAutomaticDimension 。
3,設(shè)置 estimatedRowHeight ** 的值或者實現(xiàn)預(yù)估高度的代理方法。
這是你需要知道的幾個要點,現(xiàn)在開始下載代碼,搞起項目了。
(旁白:確實很重要,雖然說了不少廢話,但是并不是浪費時間的。)
教程 App 概覽
設(shè)想一下你的老大來到你面前,對你說:“我們的用戶在為看** Deviant Artists 的方法而大聲抗議”。
我會問:"什么是 Deviant Artists "。
你的頭解釋說:“那是一個藝術(shù)家們用來分享自己作品的社交平臺。你可以通過 Deviant Art website 和 Media RSS endpoint 來了解藝術(shù)家的公告和動態(tài)?!?br>
老大:“我們開始做這個 App 吧,但是要怎么樣把內(nèi)容顯示到表格上呢?你能做到吧?”
你突然受到了感召,走進最近的電話亭,換上了披風成了super Dev..
但你不需要弄騙人的把戲做你老大的英雄,用你的編程技術(shù)就可以做到了。
(旁白:你怎么不去做導(dǎo)演啊...)
首先,現(xiàn)在客戶端代碼(項目的起始程序)這里。
(旁白:語法有一些過時,打開項目會自動讓你轉(zhuǎn)換到最新的語法,轉(zhuǎn)換完之后會報一個錯,將 ** FeedViewController.swift 里的 deselectAllRows **方法替換成如下代碼:
func deselectAllRows() {
if let selectedRows = tableView.indexPathsForSelectedRows{
for indexPath in selectedRows {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
}
}
}
)
這個項目使用的** CocoaPods ,因此打開 DeviantArtBrowser.xcworkspace ** (不是** xcodeproj 這個文件),pods 已經(jīng)包含到 zip 包里了,所以不用在重新 pod install **。
注意:如果你不清楚什么是 CocoaPods ,可以看下這個教程。
打開** Main.storyboard (在 DeviantArtBrowser ** project 下的** DeviantArtBrowser 文件夾 Views **分組里),你將會看到下面四個場景:
從左到右,它們是:
- 頂級的導(dǎo)航控制器。
- **FeedViewController **,標題是 ** Deviant Browser **。
- 還有兩個都是** DetailViewController 的場景,標題分別是Deviant Article** 和 Deviant Media ,一個只用來顯示文本,另一個文本和圖片一起顯示。
編譯并運行,你將看到一些控制臺上的輸出日志和一個短暫出現(xiàn)的活動指示器,但是在 app 里并沒有什么內(nèi)容顯示出來。
日志輸出像下面一樣:
2014-11-08 14:30:02.746 DeviantArtBrowser[70847:829282] GET 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular'
2014-11-08 14:30:03.297 DeviantArtBrowser[70847:829282] 200 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular' [0.5506 s]
app 發(fā)起一個網(wǎng)絡(luò)請求并獲得返回,但是并沒有做任何事。
(旁白:如果請求失敗,很可能是iOS9系統(tǒng)下不可用http協(xié)議,打開info.plist的源碼,粘貼如下內(nèi)容。)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
現(xiàn)在,打開** FeedViewController.swift(在 Controllers **文件夾的下面)。
看下 ** parseForQuery **這段代碼:
func parseForQuery(query: String?) {
showProgressHUD()
parser.parseRSSFeed(deviantArtBaseUrlString,
parameters: parametersForQuery(query),
success: {(let channel: RSSChannel!) -> Void in
self.convertItemPropertiesToPlainText(channel.items as! [RSSItem])
self.items = (channel.items as! [RSSItem])
self.hideProgressHUD()
self.reloadTableViewContent()
}, failure: {(let error:NSError!) -> Void in
self.hideProgressHUD()
println("Error: \(error)")
})
}
** parser 是一個 RSSParser 的實例,屬于 MediaRSSParser 的一部分。
這是一個得到 Deviant Art ** RSS feed 的網(wǎng)絡(luò)請求,它會在成功的 block 里返回一個** RSSChannel 實例。然后解析數(shù)據(jù)將 HTML 轉(zhuǎn)成普通文本, channel.items 和控制器里的 items **屬性都是數(shù)組。
** channel.items 數(shù)組包含 RSSItem **對象,每一個對象元素都是一個 RSS feed。(現(xiàn)在你該知道要將什么顯示到表格里了吧,正是 ** items **數(shù)組!)
最后,項目里會有一些** //TODO:Write this... **的注釋,是為了告訴我們需要實現(xiàn)些什么。
開始創(chuàng)建自定義 Cell
查看源代碼之后,你現(xiàn)在知道這個 app 有了不錯的數(shù)據(jù),但是什么都沒有顯示出來,要顯示它們,你需要創(chuàng)建一個自定義的 table view cell。
- 添加一個新類到** DeviantArtBrowser**項目里。
- 名字是 ** BasicCell 并且繼承自 UITableViewCell **。
- 確信** Also create xib file **沒有被勾選。
- 語言選擇** Swift 。
打開 BasicCell.swift ** 并添加如下屬性:
@IBOutlet var titleLabel: UILabel!
@IBOutlet var subtitleLabel: UILabel!
接下來,打開** Main.storyboard ,拖拽一個UITableViewCell到** FeedViewController 的 table view上。
設(shè)置 BasicCell 的 Custom Class 。
設(shè)置 BasicCell 的 Identifier (Reuse Identifier) 。
設(shè)置 cell 的 Row Height 為 83 。
拖拽一個 UILabel 到 cell 上,設(shè)置 text為 Title 。
設(shè)置 label 的 Lines 為 0,就是沒有上限。(行數(shù)可以設(shè)置很多)
接下來像下面截圖一樣設(shè)置 label 的尺寸和位置。
連接 cell 的 label 到 BasicCell 的 titleLabel ** outlet 上。
接下來,拖拽第二個 UILabel 到 cell 上,像第一個 label 一樣,并且設(shè)置 text 為** Subtitle 。
像第一個 label 一樣,按照下面的截圖來設(shè)置 Subtitle 的尺寸和位置。
設(shè)置 subtitle label 的 Color ** 為 * Light Gray Color ;字體大小為 * 15.0 ;并且 Lines **為 0 ;
將 cell 的 subtitle label 連接到 BasicCell 上的 subtitleLabel outlet上。
接下來,你將給** BasicCell **添加 auto layout ** constraints ** ,來布局 cell 。
注意:如果你對 auto layout 還不太熟悉,不清楚怎么設(shè)置 auto layout constraints ,可以看下 這個教程。
選擇 title label 且設(shè)置它的 top,trailing,leading 距離父視圖(也就是content view)20個點。確信你沒有勾選Constrain to margins。(旁白:這個屬性就是系統(tǒng)會為你默認兩邊留白)
確信 cell 的 title label 一直是:
- 向上距離20個點。
- 相對于 content view 的整體寬度,左右兩邊空出20個點。
現(xiàn)在,選擇 subtitle label 設(shè)置它的 leading,trailing,和** bottom 距離父視圖20個點。再次確認,沒有勾選Constrain to margins。
像 title label 一樣,確定在 subtitle label 上的 constraints ,是按照底部距離 content view 20個點,左右距離 content view 也是20個點。
(旁白:還是看圖更直觀一些)
技巧: 使用 auto layout 布局 UITableViewCell 時,要確定這些約束都布局到了每一個 subview 的四邊,也就是說,每一個 subview 都應(yīng)該有 leading,top,trailing 和 bottom 約束。
除此之外,** contentView 的頂部到底部都要有清晰的約束條件, 你要能確定,這些子視圖的約束可以正確的指定出 contentView **的高度。
另一部分技巧是,interface Builder 經(jīng)常在你缺失一些約束的情況下,沒有警告提示。在運行項目時,auto layout 沒有返回正確的高度,比如會返回 0 的高度,遇到這些問題需要你重新調(diào)整約束條件 直到滿足條件為止。
現(xiàn)在, 選擇 subtitle label, 按住 Control 并拖拽到 title label。選擇 Vertical Spacing 連接** subtitle label 的頂部和 title label **的底部。
在 title label 上,設(shè)置 ** Horizontal** 和 ** Vertical** 的 Content Hugging Priority 和 Content Compression Resistance Priority為 751。
在 subtitle label 上,設(shè)置 ** Horizontal** 和 ** Vertical** 的 Content Hugging Priority 和 Content Compression Resistance Priority為 750。
(旁白:解釋一下,這兩個優(yōu)先級的意思,**Content Hugging Priority **就是級別越高,越不會被拉開,抻開。 **Content Compression Resistance Priority **就是級別越高,越不會被壓縮,擠掉。這個還是要看具體的例子來理解的,有一點繞。)
這就是告訴 auto layout 怎樣去適配 labels 的文本-區(qū)分 title label 和 subtitle label 之間約束的優(yōu)先級。在這個例子里,這些約束基本滿足了條件。
檢查: 上面的約束條件滿足情況了嗎?
1,每一個子視圖的所有側(cè)邊都有約束嗎?Yes。
2,**contentView 從上到下都有約束嗎?Yes(旁白:只有這樣 contentView 才能確定出自己的高度,像 UIScrollView 一樣,才能知道自己的 contentSize,否則運行起來都不能滑動。)
**titleLabel **距頂部有20個點,它和 **subtitleLabel **之間的距離是4個點,并且 ** subtitleLabel **和底部有19.5個點。
所以,現(xiàn)在 auto layout 已經(jīng)可以動態(tài)設(shè)置 cell 的高度了。
接下來,你要創(chuàng)建一個 ** BasiceCell ** 跳轉(zhuǎn)到 **Deviant Article **場景的鏈接(segue)。
選擇你的 BasiceCell,按住 control 拖拽到 **Deviant Article 場景,從 Selection Segue **選項中選擇 **Push **。
Interface Builder 將自動更改 Accessory 屬性為 Disclosure Indicator,這是為了指示你從 cell 導(dǎo)航到詳情里去。然而,這并不符合程序的設(shè)計,選擇 **BasiceCell ** ,更改 ** Accessory **為 None。
現(xiàn)在,用戶在任何時候點擊 BasicCell 都會跳轉(zhuǎn)到 **DetailViewController **里了。
哇塞,你的** BasicCell 已經(jīng)設(shè)置完了!如果你編譯運行 app 的話,還是毛都看不到,為啥呢...(旁白:自言自語..)
還記得哪些** TODO **的注釋嗎?,對了,這就是問題所在了。你需要到哪些 TODOs 里寫一些代碼。
配置 Table View
首先,你需要配置下這個 table view。
打開** FeedViewController.swift ** ,用下面的代碼替換**configureTableView() **這個方法。
func configureTableView() {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
}
確信 table view 是用 auto layout 來動態(tài)設(shè)置高的時候,將 **rowHeight 設(shè)置為 UITableViewAutomaticDimension 。
確定這些之后,你還要提供一個 ** estimatedRowHeight 的值。小意思,160.0是一個隨意的值也可以使用。在你的項目里,你可能也想根據(jù)數(shù)據(jù)類型設(shè)置一個更好的值。
實現(xiàn) UITableView 的 Data Source
接下來,你需要實現(xiàn)** UITableViewDataSource 的協(xié)議方法。
首先,在 FeedViewController **里加上這個常量:
let basicCellIdentifier = "BasicCell"
這可以讓你用這個標識在 storyboard 里取得 ** BasicCell 。
接下來,用 tableView(_:numberOfRowsInSection:) **返回從 Deviant Art 獲取到數(shù)據(jù)個數(shù):
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
然后用,下面的代碼替換** tableView(_:cellForRowAtIndexPath:) **這個方法。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return basicCellAtIndexPath(indexPath)
}
func basicCellAtIndexPath(indexPath:NSIndexPath) -> BasicCell {
let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier) as! BasicCell
setTitleForCell(cell, indexPath: indexPath)
setSubtitleForCell(cell, indexPath: indexPath)
return cell
}
func setTitleForCell(cell:BasicCell, indexPath:NSIndexPath) {
let item = items[indexPath.row] as RSSItem
cell.titleLabel.text = item.title ?? "[No Title]"
}
func setSubtitleForCell(cell:BasicCell, indexPath:NSIndexPath) {
let item = items[indexPath.row] as RSSItem
var subtitle: NSString? = item.mediaText ?? item.mediaDescription
if let subtitle = subtitle {
// Some subtitles are really long, so only display the first 200 characters
if subtitle.length > 200 {
cell.subtitleLabel.text = "\(subtitle.substringToIndex(200))..."
} else {
cell.subtitleLabel.text = subtitle as String
}
} else {
cell.subtitleLabel.text = ""
}
}
這邊發(fā)生什么:
- 在 tableView(:cellForRowAtIndexPath:) 里,調(diào)用basicCellAtIndexPath(:) 方法獲取一個** BasicCell **。
- 在basicCellAtIndexPath(:)里獲取一個** BasicCell **,使用setTitleForCell(:indexPath:) 方法設(shè)置 title label 的text,使用** setSubtitleForCell(_:indexPath:) **方法設(shè)置 subtitle label 的text,然后return 這個 cell。
現(xiàn)在,你需要實現(xiàn)... 等等,搞定了!就這么簡單?
編譯運行,你將看到這個表格:
圖片在哪呢?
這個app看起來還不錯,但是好像感覺缺了點什么?
噢,藝術(shù)在哪呢?
Deviant Art 上都是圖片,但這個 app 沒有顯示它們,你需要去修復(fù)下這個問題,不然,你的老大會讓你失去理智!
有個方法是在你的** BasicCell **上加一個 image view。
但是在 Deviant Art 上帶圖片的信息和純文字的信息都有,所以更好的做法是新建一個自定義 cell。
增加一個繼承自** BasicCell **的新類到項目里,名字叫 ImageCell,原因是你的新 cell 也需要 titleLabel 和 subtitleLabel。因此,有必要在基類里已經(jīng)存在一些方法的時候再做所有的事嗎?
打開** ImageCell.swift ** 并且增加下面的屬性:
@IBOutlet var customImageView: UIImageView!
這個屬性的名字是** customImageView,因為在 UITableViewCell 里已經(jīng)有了一個叫 ImageView **的屬性了。
打開** Main.storyboard ,選擇 basic cell 使用 ?C ,或者從菜單里選擇 Edit > Copy **。
選擇這個 table view 并且 按下 **?V ,或者 Edit > Paste **,去創(chuàng)建一個 cell 的新 copy 。
注意:如果你操作有誤,沒有得到想要的結(jié)果,記得使用** ?Z 或者 Edit > Undo **來撤銷操作。
選擇新 cell ,更改它的Custom Class為** ImageCell **。同樣的,更改它的 Reuse Identifier ** 為 ImageCell **。
在** ImageCell 上選擇 title label,更改它的位置 x 為128**,且寬度為 172。subtitle label 也是一樣。
Interface Builder 將會有一些警告,因為這些 labels 擺放的位置和約束設(shè)置的不一樣。
正確的做法是,選擇** ImageCell 上的 title label 刪掉 leading 這條約束,subtitle label 也是一樣刪掉 leading **約束。
現(xiàn)在選擇** ImageCell 的 title label,按照下面的截圖改變它的 Intrinsic Size 的 Placeholder 的值。同樣的,改變 subtitle label 的 Intrinsic Size 。
(這是用來告訴 Interface Builder 去更新當前 view 的 frame 的占位符,Interface Builder 將不會顯示警告了。)
你需要在 cell 上增加一個 image view。但是現(xiàn)在的高度有一點小,所以選擇 ImageCell 修改它的 Row Height 為 141**。
現(xiàn)在,拖拽一個 image view 到** ImageCell **上,按照下面的截圖設(shè)置這個新 view 的位置和尺寸。
接下來,選擇這個 image view 做如下布局:
- 設(shè)置 leading,top 和 bottom 為20。
- 設(shè)置** width 和 height 為 100**。
- 確認 **Constrain to margins **沒有被勾選
-
最后點擊** Add 5 constrain **的按鈕。
選擇** image view ** 顯示它的所有約束,然后選擇它的 bottom 約束來編輯它。在屬性編輯器里,改變** Relation 為 Greater Than or Equal 它的 Priority 為 999。
同樣的,選擇 subtitle label 去顯示它的所有約束條件,然后選擇它的 bottom ,在屬性編輯器里,改變 Relation 為 Greater Than or Equal 它的 Priority**為 1000。
這是告訴 auto layout 在** imageView 和 subtitleLabel 向下的約束都為20個點的時候,打破 imageView 的,遵循 subtitleLabel 的。(旁白:因為它的優(yōu)先級高,這樣避免約束沖突。)
然后將 image view 的** height 和 width 約束的優(yōu)先級 Priority 設(shè)置成 999 。
這是因為自定義的約束有時候會和系統(tǒng)定義的約束之間會產(chǎn)生沖突,這時就是告訴 auto layout ,“如果一定要這么做,就打破這些自定義的約束吧”
(旁白:系統(tǒng)定義的優(yōu)先級高,1000。)
在大部分情況下,auto layout 都會滿足這些約束。在極少數(shù)的情況下它會打破這些約束,比如在改變設(shè)備方向時,但通常都是1-2的像素偏差,不明顯。
提示: 尤其在 table view cell上, auto layout 不總是很明顯的提示這些約束。
如果你在控制臺上看到這些 auto layout 不得不打破一個約束的警告信息,你就要試著去調(diào)整下你的約束條件的** priorities **了。
最后,選擇 ImageCell上的 title label 使用 Pin Button 來設(shè)置下 leading 約束為 8。subtitle label 也是一樣的。
現(xiàn)在你的** ImageCell **上的約束看起來是這個樣子的:
你需要選擇** ImageCell 和 image view 的 customImageView ** outlet 進行鏈接。
你需要一個** ImageCell 跳轉(zhuǎn)到 Deviant Media 場景的鏈接(segue),
這樣用戶點擊 ImageCell **就可以看到詳情了。
像之前設(shè)置 basic cell 一樣, 選擇** ImageCell ,按住 control 并拖拽到 Deviant Media 場景,然后在 Selection Segue 選項中選擇 Push **。
確認你的** Accessory 改回為 None **。
非常好,你的** ImageCell **已經(jīng)設(shè)置完成了!現(xiàn)在你可以加一些代碼讓它顯示出來了。
顯示這些圖片!
打開** FeedViewController.swift ** 在上面增加一個常量:
let imageCellIdentifier = "ImageCell"
接下來替換** tableView(_:cellForRowAtIndexPath:) **的代碼:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if hasImageAtIndexPath(indexPath) {
return imageCellAtIndexPath(indexPath)
} else {
return basicCellAtIndexPath(indexPath)
}
}
func hasImageAtIndexPath(indexPath:NSIndexPath) -> Bool {
let item = items[indexPath.row]
let mediaThumbnailArray = item.mediaThumbnails as! [RSSMediaThumbnail]
for mediaThumbnail in mediaThumbnailArray {
if mediaThumbnail.url != nil {
return true
}
}
return false
}
func imageCellAtIndexPath(indexPath:NSIndexPath) -> ImageCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier) as! ImageCell
setImageForCell(cell, indexPath: indexPath)
setTitleForCell(cell, indexPath: indexPath)
setSubtitleForCell(cell, indexPath: indexPath)
return cell
}
func setImageForCell(cell:ImageCell, indexPath:NSIndexPath) {
let item: RSSItem = items[indexPath.row]
// mediaThumbnails are generally ordered by size,
// so get the second mediaThumbnail, which is a
// "medium" sized image
var mediaThumbnail: RSSMediaThumbnail?
if item.mediaThumbnails.count >= 2 {
mediaThumbnail = item.mediaThumbnails[1] as? RSSMediaThumbnail
} else {
mediaThumbnail = (item.mediaThumbnails as NSArray).firstObject as? RSSMediaThumbnail
}
cell.customImageView.image = nil
if let url = mediaThumbnail?.url {
cell.customImageView.setImageWithURL(url)
}
}
像上面創(chuàng)建** BasicCell **一樣,但是有一點不同,有一些新的代碼:
- hasImageAtIndexPath(_:) 檢查IndexPath下的item的** mediaThumbnail的 url 不為空。如果不為空的要使用 ImageCell **來展示數(shù)據(jù)。
- **imageCellAtIndexPath(:) 和 basicCellAtIndexPath(:) 一樣,但是它要用 setImageForCell(_:indexPath:) **來設(shè)置下圖片。
- ** setImageForCell(:indexPath:) ** 嘗試獲取第二個 media thumbnail。使用 AFNetworking提供的** setImageWithURL(:) **方法來獲取圖片。
編譯運行,你將看到漂亮的藝術(shù)圖片!默認,app 檢索 “popular“ 分類的數(shù)據(jù),但你也能按照藝術(shù)家搜索。
試著輸入** by:CheshireCatAr t** 并搜索。這是我的一個朋友,Devin Kraft,他是一個杰出的畫家。(查看他的website)。
這是我很喜歡的一位,他在 Deviant Art 上很活躍,發(fā)過作品和博客。因此,他的賬號是個很好的測試賬號,可以測試帶圖片的 cell 和不帶圖片的 cell。
這個 app 已經(jīng)看起來很漂亮了,但是你還可以讓你的老大對你的技能信心提升一到兩個級別。
優(yōu)化表格
還記得很早之前設(shè)置** estimatedRowHeight 為 160.0 的時候嗎?這個屬性是在 BasicCell **的豎屏方向上工作的。但是這個值非常的不準。
你可以使用** UITableViewDelegate **提供的,在運行時給 cell 估算高度的方法替換這個值。
改之前,你需要刪除** configureTableView(): **里的一行代碼:
tableView.estimatedRowHeight = 160.0
現(xiàn)在,在** // MARK: UITextFieldDelegate **組下增加下面的方法。(實際上,你可以在這個類的任何地方增加它們,但這樣結(jié)構(gòu)比較清楚易讀。)
// MARK: UITableViewDelegate
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if isLandscapeOrientation() {
return hasImageAtIndexPath(indexPath) ? 140.0 : 120.0
} else {
return hasImageAtIndexPath(indexPath) ? 235.0 : 155.0
}
}
func isLandscapeOrientation() -> Bool {
return UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)
}
這個預(yù)估 cell 高的方法很簡單。檢查當前方向是否是橫屏并且如果當前 index path 有圖片的話,返回一個預(yù)先裁定的值。
提示:不論你是實現(xiàn)的代理方法,還是簡單的設(shè)置了一個** estimatedRowHeight 固定的值。
這個 table view 會在代理方法和 estimatedRowHeight 的值之中選擇一個。它會影響到滑動的指示條和滑動的性能。(是否卡頓)
如果你的 cell 預(yù)估的高度不太正確,那么在滑動的時候就會卡頓,滑動指示條不太準,內(nèi)容也會混亂。
如果你的預(yù)估是準確的,計算就會慢,table view 滾動也會變慢。
這個成功的關(guān)鍵就在于在準確和不準確之間找一個平衡,減少不必要的計算成本。
(旁白:難道自己慢慢調(diào)試這個數(shù)值嗎?)
你還是使用固定的值來估算的高度,但是現(xiàn)在可以根據(jù) cell 的類型和設(shè)備的方向來設(shè)置更合理的高度了。你可以設(shè)置自己感興趣的值,但是記住訣竅就是能讓它計算的更快就行。
編譯并運行,你應(yīng)該能看到 table view 滑動的很流暢,看起來很棒。
這就是方法的最后實現(xiàn),這個 app 現(xiàn)在完成了!
從這去哪呢
你可以去下載完成項目,在這
(旁白:上面的路徑下載下來的項目編譯會報錯,因為它是用Xcode6.3和Swift1.2做的,轉(zhuǎn)換到最新語法之后,在像上面已經(jīng)提到過的修改一個方法的代碼,添加http白名單。你也可以下載這里已經(jīng)修改過的代碼)
Table views 可能是iOS里面組織數(shù)據(jù)視圖中最常用的了。你的 apps 會很復(fù)雜,你可能要使用各種類型的自定義 cell 來布局。幸運的是 iOS8 和 auto layout 能很容易的做到這些。
(旁白:不幸的是,有多少 app 是只支持iOS8以上的呢。)
如果你有一些問題或建議,請在下面留言。