摘要 前言 最近又要作新功能了,雖然沒有什麼難點,只是獲取後端XML數據顯示到TableView,可是不是能夠更簡單快速的完成呢?原來Cell的動態高度一直都是經過sizeWithFont手動計算,潛意識以爲這應該不是最好的實現方式,但因爲當時時間緊不容許嘗試新技術,因此問題也就遺留了下來,此次又遇到了,時間充裕就解決下吧。 Autolayout是解決自適應frame問題的解決方案(iOS6.0就已經支持了,我如今才用= =#)。經過給視圖元素設置合適的約束條件,內部會根據元素內容和限制條件計算出合適的尺寸顯示。咱們就不用本身手動寫這些代碼了。 文章步驟看上去有些複雜,真正作起來仍是很快...後端
首先將Cell作好佈局,調整到滿意的位置和寬度,而後開始作Autolayout設定。工具
Autolayout操做方式有兩種,一種是選擇目標後,使用右下角的工具欄;另外一種是直接使用右鍵拖拽目標,在彈出的菜單中選擇限制項。當選擇的目標比較小的時候,能夠打開左側的菜單,在這裏作拖拽操做同樣是能夠的。我的感受後者更方便一些。佈局
開始以前,先來介紹下使用的基本工具吧。性能
第一個按鈕是和對齊有關的,就是控制多個元素(Lable, Button等)的統一約束。例如咱們須要讓標題和內容按照左,就選擇標題和內容元素,選擇Leading Edges設置爲5便可。測試
第二個按鈕是和元素位置固定有關的限制條件,直接看圖吧:ui
右側可以看到當前選擇元素限制條件的列表:spa
這裏有兩個參數,「Content Hugging Priority」和「Content Compression Resistance Priority」,感受不太好理解,棧爆上找到一篇解釋,講的挺好的:Cocoa Autolayout: content hugging vs content compression resistance priority.net
有時候想要一個元素的間距是一個動態值,例如距離右側至少10pt(即>=10pt),那麼能夠在上圖中點擊右側按鈕(齒輪)進入詳細設置:blog
第三個按鈕是有關清除限制條件、根據限制更新視圖大小的工具。我的比較經常使用的是清除限制條件,有時候設置錯了很麻煩,直接清除掉從新來就好了。
上面這些就是經常使用到的一些限制條件了。我的以爲使用右鍵拖拽彈出的菜單選擇更方便和直觀一些,由於菜單中會根據拖拽內容動態顯示可用項供咱們選擇,菜單如圖
大體就是這些了吧……
我來談談本身的用法。整體上是從上到下,從左到右作約束限制。在這個例子中,就是設置標題->內容->發帖人這樣的順序。
設置標題的頂部和左側距離,以及寬度(防止超出邊界)。
設置內容的頂部(距離標題)和左側距離,以及寬度。設置最大行數。
設置發帖人的頂部和左側距離,以及高度。
設置發帖時間的頂部和左側距離,距離右側間距(防止內容過長)。
關鍵步驟,設置發帖人距離底部距離,若是不設置這個參數,那麼下面代碼計算的Cell高度會永遠是0。
多試一試,若是有錯誤或者缺乏限制,XCode會有提示。它報出的錯誤通常都是必須修正的,但它給的自動修正建議有時並非咱們想要的(正確的),想清楚再添加。
使用了xib製做的Cell,那麼在原來的項目代碼中如何使用呢?看代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
static NSString *CellIdentifier = @"CellIdentifier";
- (void)viewDidLoad { //註冊TableView中用於複用的Cell [self.tableView registerNib:[UINib nibWithNibName:@"BBSPostContentCell" bundle:nil] forCellReuseIdentifier:CellIdentifier]; //... }
//關鍵方法,獲取複用的Cell後模擬賦值,而後取得Cell高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { BBSPostContentCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *dataSourceItem = [self.dataSource objectAtIndex:indexPath.row]; cell.titleLabel.text = [dataSourceItem valueForKey:@"title"]; cell.contentLabel.text = [dataSourceItem valueForKey:@"body"];
[cell setNeedsUpdateConstraints]; [cell updateConstraintsIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height; }
//在cellForRowAtIndexPath中,按照常規方法作賦值就好了 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { BBSPostContentCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *dic = dataSource[indexPath.row]; cell.titleLabel.text = dic[@"title"]; cell.contentLabel.text = dic[@"body"];
return cell; } |
2014.1.2: 在測試時發現這部分的代碼還存在一些性能問題(整個表視圖在更新時會卡頓),我會稍後補上。
我在使用Instruments分析發現,heightForRowAtIndexPath中調用dequeueReusableCellWithIdentifier會佔用不少CPU資源,所以我試着不使用registerNib方法註冊複用Cell,而在代碼中手動處理,相似這樣:
1 2 3 4 5 6 7 |
BBSPostContentCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[NSBundle mainBundle] loadNibNamed:@"BBSPostContentCell" owner:self options:NULL][0]; NSLog(@"cell loadNibNamed"); } else { NSLog(@"cell dequeueReusableCellWithIdentifier"); } |
這時我發現這裏的Cell調用dequeueReusableCellWithIdentifier方法老是返回nil,所以每次都是從xib中加載,從而耗費了大量的資源。問題的緣由我還不清楚,目前個人解決方法是,單獨生成一個Cell用於在heightForRowAtIndexPath方法中計算高度。
其次,在[tableView reloadData]和[tableView insertRowsAtIndexPaths]時,底層會將全部行高從新計算,這個會佔用大量的時間,所以我試着對行高作了緩存,暫時解決了這個問題。
因爲Autolayout只能在iOS6.0以上版本使用,而根據友盟統計,目前6.0如下的用戶大概還有8%左右(2013.12)。如今有兩個辦法解決:
哥不在意,放棄這些用戶!(好霸氣=。=)把項目的部署版本修改成6.0以上便可。
咳…咳…這個嘛,用戶仍是有必要支持的………恩,那咱們來講說這個怎麼兼容。
思路很簡單,咱們告訴XCode,6.0以上版本使用Autolayout,如下的舊版本不要使用這個就能夠了。
將原xib文件inspector中選擇」Interface Builder Document」->」Build for」->」iOS 6.0 and Later」,告訴XCode,這個xib在6.0以上設備編譯。
將xib文件拷貝一份副本,命名爲」xxx_iOS5.xib」,在inspector中選擇」Project Deployment Target」,也就是說使用項目部署目標版本(即最低版本5.0),並取消」Use Autolayout」選項。
在代碼中根據系統版本加載不一樣的xib文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) \ ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define IS_SUPPORT_AUTOLAYOUT SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")
- (void)viewDidLoad { if (!IS_SUPPORT_AUTOLAYOUT) { //for iOS 5.x [self.tableView registerNib:[UINib nibWithNibName:@"BBSPostContentCell_iOS5" bundle:nil] forCellReuseIdentifier:CellIdentifier]; } else { [self.tableView registerNib:[UINib nibWithNibName:@"BBSPostContentCell" bundle:nil] forCellReuseIdentifier:CellIdentifier]; } } |
最後別忘了在高度計算時,區分下代碼:
1 2 3 4 5 6 7 8 9 10 11 12 |
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (IS_SUPPORT_AUTOLAYOUT) { //Autolayout部分代碼,同上 //..... return height; } else { //for iOS 5.x //爲了簡單起見,就直接使用固定值了,固然若是你要本身爲iOS5用戶手動計算動態高度,也是能夠的。 return 81; } } |