zoukankan      html  css  js  c++  java
  • 转 使用Autolayout xib实现动态高度的TableViewCell

    摘要 前言 最近又要做新功能了,虽然没有什么难点,只是获取后端XML数据显示到TableView,但是不是可以更简单快速的完成呢?原来Cell的动态高度一直都是通过sizeWithFont手动计算,潜意识觉得这应该不是最好的实现方式,但由于当时时间紧不允许尝试新技术,所以问题也就遗留了下来,这次又遇到了,时间充裕就解决下吧。 Autolayout是解决自适应frame问题的解决方案(iOS6.0就已经支持了,我现在才用= =#)。通过给视图元素设置合适的约束条件,内部会根据元素内容和限制条件计算出合适的尺寸显示。我们就不用自己手动写这些代码了。 文章步骤看上去有些复杂,真正做起来还是很快...

     

     
     
     
     

    创建Xib文件

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

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

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

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

    Autolayout_Align

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

    Autolayout_Pin

    右侧能够看到当前选择元素限制条件的列表:

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

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

    Autolayout_Constraint_Relation Config

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

    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;

        }

    }

    原文链接:http://my.oschina.net/u/2360693/blog/481236?p={{totalPage}}
  • 相关阅读:
    centos 安装 TortoiseSVN svn 客户端
    linux 定时任务 日志记录
    centos6.5 安装PHP7.0支持nginx
    linux root 用户 定时任务添加
    composer 一些使用说明
    laravel cookie写入
    laravel composer 安装指定版本以及基本的配置
    mysql 删除重复记录语句
    linux php redis 扩展安装
    linux php 安装 memcache 扩展
  • 原文地址:https://www.cnblogs.com/sungk/p/5085161.html
Copyright © 2011-2022 走看看