這篇文章我們來講一下UITableView的cell自適應高度,以及遇到的問題的解決辦法。在看文章之前希望你已經會UITableView的基本使用了。
先奉上這篇文章的demo的Github地址:UITableViewCellHeightDemo。大家可以下載下來和文章配合看。
cell高度計算的歷史
在iOS8之前,如果UITableViewCell的高度是動態的,如果想要顯示正確的話,我們需要在下面這個UITableView的代理方法中,返回每一行的精確高度:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
如果cell的控件很多,樣式很復雜的話,在這里面我們就可能需要寫很多代碼去做一些復雜的計算,甚至可能導致滑動不流暢。
后來也有一些人寫了一些第三方去解決這個問題,例如UITableView-FDTemplateLayoutCell。只要給cell自上而下加好約束,它就可以幫我們去算cell的高度並且可以緩存,省去了我們自己寫計算代碼的成本。具體可以進鏈接里面看看它的demo。
但是在iOS10的系統下, FDTemplateLayoutCell
會卡界面,而且tableview的行數越多表現的越卡。
而且蘋果在iOS8之后,推出了一種超級簡單的cell動態自適應的方法,使用起來比 FDTemplateLayoutCell
也簡單一些,而且現在iOS10都出來了,沒有必要去支持iOS7了,所以最后我還是選擇了用系統的辦法。這樣我們以后就再也不用寫heightForRowAtIndexPath
方法了哈哈哈。
系統的cell自適應高度的使用方法
首先我們需要把cell上的控件自上而下加好約束,如果對約束不熟悉的話建議看看下面這兩篇文章學習一下:
Auto Layout Tutorial in iOS 9 Part 1: Getting Started(http://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2)
[Auto Layout Tutorial in iOS 9 Part 2: Constraints
用xib加約束和用masonry加代碼約束都是可以的。注意約束一定要自上而下加好,讓系統知道怎么去計算高度。在這篇文章的demo里面的cell加的約束是這樣的:

加好約束后,然后告訴tableView自己去適應高度就可以了。有兩種寫法:
self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = 100;
或者直接寫這個代理方法就可以了
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 100; }
這個的意思就是告訴tableView,你需要自己適應高度,我不給你算啦哈哈哈。但是我們需要告訴它一個大概高度,例如上面的100,理論上這個是可以隨便寫的,並不影響顯示結果,但是越接近真實高度越好。
來看下demo效果:

我們看到,cell已經自己適應內容算出了高度,是不是很方便呢哼哼。
具體的代碼大家可以去demo看哦。
其實section的header和footer也是可以自動適應的,對應的方法有:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section; - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section;
但是我們在實際開發中,一般都是根本沒有header和footer,有的話一般也是給一個固定高度。所以在這里就不講解了,原理都一樣。
可能遇到的問題和解決辦法
1.高度不對
有時候有可能運行出來后看到cell的高度顯示的不對,就像這樣:

這個問題是因為約束沒有滿足自上而下,從而系統不知道怎么去計算。解決辦法就是去修改約束,直到滿足為止。一定要好好理解約束啊!
2.點擊狀態欄無法滾動到頂部
我們知道,如果界面中有UIScrollView的話,點擊狀態欄會讓其滾動到頂部,就像這樣:

但是如果我們用了自動計算高度的方法,又調用了tableView的reloadData方法(例如我們的數據有分頁的時候,加載完下一頁的數據后會去刷新tableView)。這時候就會出現問題,點擊狀態欄就有幾率不能精確滾動到頂部了:

解決這個問題的辦法是去緩存cell的高度,代碼如下:
@property (nonatomic, strong) NSMutableDictionary *heightAtIndexPath;//緩存高度所用字典
#pragma mark - UITableViewDelegate -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = [self.heightAtIndexPath objectForKey:indexPath]; if(height) { return height.floatValue; } else { return 100; } } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = @(cell.frame.size.height); [self.heightAtIndexPath setObject:height forKey:indexPath]; }
解釋一下,就是用一個字典做容器,在cell將要顯示的時候在字典中保存這行cell的高度。然后在調用estimatedHeightForRowAtIndexPath方法時,先去字典查看有沒有緩存高度,有就返回,沒有就返回一個大概高度。
緩存高度之后,在demo里面多試幾次,發現點擊狀態欄已經可以精確滾動回頂部了:

這段代碼其實可以寫在viewController的基類里面,這樣寫一遍就可以每個地方都能緩存cell的高度了。詳見demo。這樣就完美了!
原文鏈接:http://www.jianshu.com/p/64f0e1557562
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。