iOS 8 自適應 Cell

來源:互聯網
上載者:User

標籤:

在使用 table view 的時侯經常會遇到這樣的需求:table view 的 cell 中的內容是動態,導致在開發的時候不知道一個 cell 的高度具體是多少,所以需要提供一個計算 cell 高度的演算法,在每次載入到這個 cell 的時候計算出 cell 真正的高度。

在 iOS 8 之前

沒有使用 Autolayout 的情況下,需要實現 table view delegate 的 tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 方法,在這個方法中計算並返回 cell 的高度。比如,我有一個可以顯示任意行數的純文字 cell,計算 cell 的代碼可以是這樣:

123456789101112 override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {    let content = self.datas[indexPath.row] as String    let padding: CGFloat = 20    let width = tableView.frame.size.width - padding * 2;    let size = CGSizeMake(width, CGFloat.max)    let attributes = [NSFontAttributeName: UIFont(name: "Helvetica", size: 14)!]    let frame = content.boundingRectWithSize(size,        options: NSStringDrawingOptions.UsesLineFragmentOrigin,        attributes: attributes,        context: nil)    return frame.size.height+1;}

上面的代碼是一個最簡單的例子,這個例子看起來好像沒有什麼問題。但是通過查看這個 delegate 方法的文檔後,可以知道,在每次 reload tableview 的時候,程式會先計算出每一個 cell 的高度,等所有高度計算完畢,確定了 tableview 的總的高度後,才開始渲染視圖並顯示在螢幕上。這意味著在顯示 table view 之前需要執行一堆的計算,並且這是在主線程中進行的,如果計算量太大程式就很有可能出現卡頓感。比如: table view 的資料有上千條,或者計算高度的代碼中還要先擷取圖片再根據圖片計算高度,這些操作都是非常慢的。

如果在 cell 中使用了 autolayout,在計算 cell 高度時會更麻煩。有興趣的可以看這裡有篇關於如何在 autolayout 下動態計算高度的文章。

為什麼不能等滾動到某個 cell 的時候,再調用計算這個 cell 高度的 delegate 呢?原因是 tableview 需要獲得它的內容的總高度,用這個高度去確定捲軸的大小等。直到 iOS 7 UITableViewDelegate中添加了新的 API:

1 tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat

這個方法用於返回一個 cell 的預估高度,如果在程式中實現了這個方法,tableview 首次載入的時候就不會調用heightForRowAtIndexPath 方法,而是用 estimatedHeightForRowAtIndexPath 返回的預估高度計算 tableview 的總高度,然後 tableview 就可以顯示出來了,等到 cell 可見的時候,再去調用heightForRowAtIndexPath 擷取 cell 的正確高度。

通過使用estimatedHeightForRowAtIndexPath 這個 Delegate 方法,解決了首次載入 table view 出現的效能問題。但還有一個麻煩的問題,就是在 cell 沒有被載入的時候計算 cell 的高度,上面給出的代碼中,僅僅是計算一個 NSString 的高度,就需要不少代碼了。這種計算實際上是必須的,然而在 iOS 8 開始,你可能可以不用再寫這些煩人的計算代碼了!

iOS 8 的魔法

在 iOS 8 中,self size cell 提供了這樣一種機制:cell 如果有一個確定的寬度/高度,autolayout 會自動根據 cell 中的內容計算出對應的高度/寬度。

TableView 中的 cell 自適應

要讓 table view 的 cell 自適應內容,有幾個要點:

1.設定的 AutoLayout 約束必須讓 cell 的 contentView 知道如何自動延展。關鍵點是 contentView 的 4 個邊都要設定串連到內容的約束,並且內容是會動態改變尺寸的。

2.UITableView 的 rowHeight 的值要設定為 UITableViewAutomaticDimension

3.和 iOS 7 一樣,可以實現 estimatedHeightForRowAtIndexPath 方法提升 table view 的第一次載入速度。

4.任何時候 cell 的 intrinsicContentSize 改變了(比如 table view 的寬度變了),都必須重新載入 table view 以更新 cell。

例子

在 Xcode 中建立一個項目,在 storyboard 中建立一個 UITableViewController 的 IB,建立一個如下樣子的 cell:

這個 cell 中有 3 個元素,其中 imageView 的 autoLayout 約束為:

imageView 左邊離 contentView 左邊 0

imageView 上邊離 contentView 上邊 0

imageView 的 width 和 height 為 80

imageView 下邊離 contentView 下邊大於等於 0(為了防止內容太少,導致 cell 高度小於圖片高度)

titleLabel 的 autoLayout 約束為:

titleLabel 左邊離 imageView 右邊 8

titleLabel 上邊和 imageView 上邊在同一隻線上

titleLabel 右邊離 contentView 右邊 0

titleLabel 下邊離 description 上邊 8

titleLabel 的高度小於等於 22,優先順序為 250

descriptionLabel 的約束為:

descriptionLabel 左邊和 titleLabel 左邊在同一直線上

descriptionLabel 上邊裡 titleLabel 8

descriptionLabel 下邊裡 contentView 下邊 0

descriptionLabel 右邊離 contentView 右邊 0

然後在這個 IB 對應的 UITableViewController 中載入一些資料進去,顯示效果

實現這個效果,我除了設定了 autoLayout,還設定了 tableView 的 rowHeight = UITableViewAutomaticDimension,然後就是這樣了。一點計算 cell 高度的代碼都沒有!!我連 heightForRowAtIndexPath都不用實現,真的是….爽出味啊!所以如果已經在開發 iOS 8 Only 的應用了一定要用autolayout,把煩人的計算交給 autolayout 去吧。

CollectionView 中的 cell 自適應

在 collection view 中也能讓 cell 自適應內容大小,如果 UICollectionView 的 layout 是一個 UICollectionViewFlowLayout,只需要將 layout.itemSize = ... 改成 layout.estimatedItemSize = ...。 只要設定了 layout 的 estimatedItemSize,collection view 就會根據 cell 裡面的 autolayout 約束去確定cell 的大小。

原理:

1.collection view 根據 layout 的 estimatedItemSize 算出估計的 contentSize,有了 contentSize collection view 就開始顯示

2.collection view 在顯示的過程中,即將被顯示的 cell 根據 autolayout 的約束算出自適應內容的 size

3.layout 從 collection view 裡擷取更新過的 size attribute

4.layout 返回最終的 size attribute 給 collection view

5.collection 使用這個最終的 size attribute 展示 cell

總結

這次 iOS 8 的發布對 UI 開發來說是越來方便了,很多以前需要寫大量計算的代碼現在都可以通過拖拖 IB 上的 UI 控制項就可以實現了,當然首先你要會 autolayout。 如果很幸運的在開發 iOS 8 only 的應用,真的可以刪除heightForRowAtIndexPath中那些繁重的計算代碼了!讓 autolayout 幫我們完成所有的工作吧。

  

iOS 8 自適應 Cell

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.