iOS 8 具有可变高度的自调整单元格

Posted

技术标签:

【中文标题】iOS 8 具有可变高度的自调整单元格【英文标题】:iOS 8 self sizing cells with changing height 【发布时间】:2015-05-26 21:03:01 【问题描述】:

我使用自定义 UITableViewCells 创建了一个 tableview

tableview.rowHeight = UITableViewAutomaticDimension;

为了测试创建了一个 UIView 并将其添加到 cell.contentView 并具有以下约束。 (使用砖石)

- (void)updateConstraints 
    UIView *superview = self.contentView;

    if (!_didSetupConstraints) 
        [self.testView mas_makeConstraints:^(MASConstraintMaker *make) 
            make.top.equalTo(superview);
            make.height.equalTo(@10.0f);
            make.left.equalTo(superview);
            make.right.equalTo(superview);
            make.bottom.equalTo(superview);
        ];

        _didSetupConstraints = YES;
    

    [super updateConstraints];

当我选择一行时,我喜欢更改所选单元格的高度。我这样做:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated 
    [super setSelected:selected animated:animated];

    [self.testView mas_updateConstraints:^(MASConstraintMaker *make) 
        if (selected) 
            make.height.equalTo(@100);
        
        else 
            make.height.equalTo(@10);
        
    ];

    [UIView animateWithDuration:0.3f animations:^
        [super layoutIfNeeded];
    ];

这似乎有效,但是,xcode 抱怨以下错误:

Unable to simultaneously satisfy constraints.

那么问题是contentView 正在自己创建一个高度约束(因为默认情况下是contentView.translatesAutoresizingMaskIntoConstraints = YES;)。

所以当 updateConstraints 首次运行时,系统会在contentView 上创建一个高度约束,等于10.0f

之后,当setSelected... 被调用时,我会改变我的testView 的高度,因此testView.height (100.0f) 和contentView.height (10.0f) 之间存在冲突,因为testView 附加到底部contentView(为了让自定大小的单元格工作)它给出了错误。

我尝试设置contentView.translatesAutoresizingMaskIntoConstraints = NO;,但 UITableViewCell 似乎无法真正确定单元格的合适大小。 (有时它太宽/太小)等等。

实现可以动态改变高度的自定尺寸单元格的正确方法是什么?

【问题讨论】:

您是否设置了estimatedRowHeight 属性? 我没有设置estimatedRowHeight :) 但只是测试过,它对高度限制冲突的问题没有任何影响。 【参考方案1】:

这里有两个问题:

1.自动布局消息:Unable to simultaneously satisfy constraints.

这是因为您更新了自定义视图的height 约束,但没有更新contentViewheight 约束。因为你还设置了自定义视图的bottom约束到contentViewbottom,所以系统不能同时满足这两个约束:自定义视图不能有100的高度,同时连接到contentView的底部,而contentView仍然有高度为 10。

解决这个问题很容易。您为 contentView 定义高度约束并将其设置为自定义视图的高度:

[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) 
    make.height.equalTo(self.testView);
];

这解决了问题。

2。更新单元格的高度

更新单元格高度并不容易,因为单元格高度由UITableViewController 处理,而不是单元格本身。当然,您可以更改单元格的高度,但只要UITableViewController 没有为单元格提供足够的空间,您就不会看到新的单元格高度。您会看到现在较高的单元格将与下一个单元格重叠。

所以你需要一种方法来告诉UITableViewController 它应该更新你的单元格的高度。您可以通过调用reloadRowsAtIndexPaths:withRowAnimation: 来做到这一点。这会重新加载单元格并很好地对其进行动画处理。所以单元必须以某种方式在UITableViewController 上调用该方法。那里的挑战:UITableViewCell 没有引用它的UITableViewController(它不应该)。

你可以做的是子类UITableViewCell,并给它一个闭包属性,它可以在它的高度发生变化时执行:

class DynamicHeightTableViewCell: UITableViewCell 
    var heightDidChange: (() -> Void)?
    ...

现在,当您将 UITableViewControllerDataSource 中的单元格出列时,您设置了它的关闭:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
    let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("DynamicHeightTableViewCell", forIndexPath: indexPath)
    if let customCell = cell as? DynamicHeightTableViewCell 
        customCell.heightDidChange =  [weak cell, weak tableView] in
            if let currentIndexPath = tableView?.indexPathForCell(cell!) 
                tableView?.reloadRowsAtIndexPaths([currentIndexPath], withRowAnimation: .Automatic)
            
        
    

    return cell

然后当单元格改变它的高度时,你只需执行闭包,单元格就会被重新加载:

func theFunctionWhereTheHeightChanges() 

   // change the height

   heightDidChange?()

【讨论】:

对我不起作用(reloadRowsAtIndexPaths 上可能会发生崩溃),并且将 testView 链接到 contentView 的约束也可能触发 Autolayout 冲突。

以上是关于iOS 8 具有可变高度的自调整单元格的主要内容,如果未能解决你的问题,请参考以下文章

具有自动调整大小的单元格的自定义 UICollectionViewLayout 会因估计的项目高度较大而中断

自动布局:具有可变高度和两个标签的表格单元格

具有自动布局的自定义单元格的动态高度

如何在 iPhone 上正确设置可变高度表格单元格?

使用 UITableView 的可变高度自定义单元格调用 reloadData 时保持 UITableView 的当前位置

使用最小高度单元格自动调整表格视图