使用Autolayout xib实现动态高度的TableViewCell

http://my.oschina.net/u/2360693/blog/481236?p={{totalPage}}html

建立Xib文件

首先将Cell作好布局,调整到满意的位置和宽度,而后开始作Autolayout设定。缓存

Autolayout操做方式有两种,一种是选择目标后,使用右下角的工具栏;另外一种是直接使用右键拖拽目标,在弹出的菜单中选择限制项。当选择的目标比较小的时候,能够打开左侧的菜单,在这里作拖拽操做同样是能够的。我的感受后者更方便一些。工具

开始以前,先来介绍下使用的基本工具吧。布局

 

第一个按钮是和对齐有关的,就是控制多个元素(Lable, Button等)的统一约束。例如咱们须要让标题和内容按照左,就选择标题和内容元素,选择Leading Edges设置为5便可。性能

Autolayout_Align

第二个按钮是和元素位置固定有关的限制条件,直接看图吧:测试

Autolayout_Pin

右侧可以看到当前选择元素限制条件的列表:ui

Autolayout_Inspector_Constrainsts list
这里有两个参数,“Content Hugging Priority”和“Content Compression Resistance Priority”,感受不太好理解,栈爆上找到一篇解释,讲的挺好的:Cocoa Autolayout: content hugging vs content compression resistance priority.net

有时候想要一个元素的间距是一个动态值,例如距离右侧至少10pt(即>=10pt),那么能够在上图中点击右侧按钮(齿轮)进入详细设置:htm

Autolayout_Constraint_Relation Config

第三个按钮是有关清除限制条件、根据限制更新视图大小的工具。我的比较经常使用的是清除限制条件,有时候设置错了很麻烦,直接清除掉从新来就好了。blog

Autolayout_Resolve Auto Layout Issues

上面这些就是经常使用到的一些限制条件了。我的以为使用右键拖拽弹出的菜单选择更方便和直观一些,由于菜单中会根据拖拽内容动态显示可用项供咱们选择,菜单如图

Autolayout_ShortAction

大体就是这些了吧……

我来谈谈本身的用法。整体上是从上到下,从左到右作约束限制。在这个例子中,就是设置标题->内容->发帖人这样的顺序。

Autolayout_Example

  1. 设置标题的顶部和左侧距离,以及宽度(防止超出边界)。

  2. 设置内容的顶部(距离标题)和左侧距离,以及宽度。设置最大行数。

  3. 设置发帖人的顶部和左侧距离,以及高度。

  4. 设置发帖时间的顶部和左侧距离,距离右侧间距(防止内容过长)。

  5. 关键步骤,设置发帖人距离底部距离,若是不设置这个参数,那么下面代码计算的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)。如今有两个办法解决:

  1. 哥不在意,放弃这些用户!(好霸气=。=)把项目的部署版本修改成6.0以上便可。

  2. 咳…咳…这个嘛,用户仍是有必要支持的………恩,那咱们来讲说这个怎么兼容。

思路很简单,咱们告诉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;

    }

}

完成了!

2013的最后一篇文章,元旦快乐! :)

原创文章,采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
转载请注明:转载自 Tony's blog,原文网址:http://itony.me/381.html

 

相关文章
相关标签/搜索