【问题标题】:How to animate UITableViewCell height using auto-layout?如何使用自动布局为 UITableViewCell 高度设置动画?
【发布时间】:2024-01-03 19:37:01
【问题描述】:

Stack Overflow 上有很多关于UITableViewCell 高度动画的类似问题,但对于新的 iOS8 自动布局驱动的表格视图没有任何作用。我的问题:

自定义单元格:

@property (weak, nonatomic) IBOutlet iCarousel *carouselView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
@property (weak, nonatomic) IBOutlet UIButton *seeAllButton;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *carouselHeightConstraint;

注意carouselHeightConstraint。这是内容视图的子视图(唯一的子视图)的高度限制。

例如,高度设置为230.0f。在第一次加载时,它看起来像:

然后,在 See All 操作中我想展开单元格,我的代码:

- (IBAction)seeAllButtonAction:(id)sender {
  
  BOOL vertical = !self.carouselCell.carouselView.vertical;
  self.carouselCell.carouselView.type = vertical ? iCarouselTypeCylinder : iCarouselTypeLinear;
  self.carouselCell.carouselView.vertical = vertical;

  [UIView transitionWithView:self.carouselCell.carouselView
                    duration:0.2
                     options:0
                  animations:^{
  
    self.carouselCell.carouselHeightConstraint.constant = vertical ? CGRectGetHeight(self.tableView.frame) : 230;
    [self.carouselCell setNeedsUpdateConstraints];
    [self.carouselCell updateConstraintsIfNeeded];
    [self.tableView beginUpdates];
    [self.tableView endUpdates];

                  completion:^(BOOL finished) {
                  }];
}

如您所见,我尝试使用这个好的旧解决方案:
Can you animate a height change on a UITableViewCell when selected?

结果:

我的单元格缩小44.0f高度。

问题:

为什么会这样?我希望看到我的细胞在自动布局的魔力下顺利扩展。

注意:
我不想使用-tableView:heightForRowAtIndexPath:。现在是自动布局时代,对吧?

【问题讨论】:

  • 您仍然需要使用tableView:heightForRowAtIndexPath:。这不是自动布局工作。
  • @Astoria,看起来很像自动布局工作。
  • @orkenstein 我完全同意现在应该使用自动布局来完成。我正在尝试做基本相同的事情:在单元格的 contentView 上设置约束并告诉表格视图为高度设置动画。但是,到目前为止,我无法让它工作。您找到解决此问题的方法了吗?
  • @Alex,我最终使用了tableView:heightForRowAtIndexPath:。它现在看起来是唯一的解决方案。悲伤但真实。不要忘记正确更新约束。
  • @orkenstein 在发布该评论后,我确实设法解决了我的问题,并且我使用了纯粹的自动布局方法:不使用tableView:heightForRowAtIndexPath:。如果您仍然感兴趣,可以查看我的解决方案here(ExpandableTableViewCell 和示例项目)。不过,它仍在进行中(但它确实有效)。

标签: ios objective-c uitableview ios8 autolayout


【解决方案1】:

以下内容对我有用:

准备工作:

  1. viewDidLoad 上告诉表格视图使用自动调整大小的单元格:

    tableView.rowHeight = UITableViewAutomaticDimension;
    tableView.estimatedRowHeight = 44; // Some average height of your cells
    
  2. 像往常一样添加约束,但将它们添加到单元格的contentView!

动画高度变化:

假设您更改了约束的constant

myConstraint.constant = newValue;

...或者您添加/删除约束。

要使此更改动画化,请执行以下操作:

  1. 告诉 contentView 动画变化:

    [UIView animateWithDuration: 0.3 animations: ^{ [cell.contentView layoutIfNeeded] }]; // Or self.contentView if you're doing this from your own cell subclass
    
  2. 然后通过动画告诉表格视图对高度变化做出反应

    [tableView beginUpdates];
    [tableView endUpdates];
    

第一步的 0.3 的持续时间似乎是 UITableView 在调用 begin/endUpdates 时用于其动画的持续时间。

奖励 - 无需动画且无需重新加载整个表格即可更改高度:

如果您想执行与上述相同的操作,但没有动画,请改为执行以下操作:

[cell.contentView layoutIfNeeded];
[UIView setAnimationsEnabled: FALSE];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled: TRUE];

总结:

// Height changing code here:
// ...

if (animate) {
    [UIView animateWithDuration: 0.3 animations: ^{ [cell.contentView layoutIfNeeded]; }];
    [tableView beginUpdates];
    [tableView endUpdates];
}
else {
    [cell.contentView layoutIfNeeded];
    [UIView setAnimationsEnabled: FALSE];
    [tableView beginUpdates];
    [tableView endUpdates];
    [UIView setAnimationsEnabled: TRUE];
}

您可以查看我的单元格实现,当用户选择它时会展开 here(纯 Swift 和纯自动布局 - 真正的未来方式)。

【讨论】:

  • 在非动画版本中:不会在表格视图上使用UITableViewRowAnimationNone 调用reloadRowsAtIndexPaths:withRowAnimation: 吗?而不是在 UIViewbeginUpdates / endUpdates 上禁用动画。
  • @MichałCiuba 我不记得具体尝试过,但我记得当时在想:“似乎reloadRowsAtIndexPaths:withRowAnimation: 不会重新加载单元格高度。我想我必须尝试其他方法"(它可能是为高度设置动画,但不是即时更新)。
  • 是的,我的意思是:首先调用layoutIfNeeded 来更新单元格的高度,然后调用reloadRowsAtIndexPaths:withRowAnimation: 来更新表格视图。
  • 虽然这很好,但请注意这只动画到动画的最终状态,而不是介于两者之间。
  • 我想强调[tableView beginUpdates]; [tableView endUpdates]; 非常重要,否则您将在控制台中遇到没有多大意义的约束错误。
【解决方案2】:

我想对 Alex 的回答补充几点。

如果您在 cellForRowAtIndexPath 或 willDisplayCell 中调用 beginUpdates 和 endUpdates,您的应用将崩溃。在对单元格的增加进行动画处理后,您可能希望它在滚动到 tableView 后保持不变。您可能还希望其他单元格的高度保持不变。但请记住,单元格是可重复使用的,因此您最终可能会得到其他单元格的高度增加。

因此,您必须根据它所代表的项目在 cellForRowAtIndexPath 内设置约束常量。但是,如果您之后调用 layoutIfNeeded,它将显示带有约束的警告。我相信它会在计算新高度之前尝试生成单元格布局。

public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
    myConstraint.constant = itemForCell.value == "x" ? 50 : 20 // stop right here
    cell.layoutIfNeeded() <- don't do this or some of your constraints will break
    beginUpdates() <-- if you do this your app will crash
    endUpdates() <-- same here

【讨论】:

    最近更新 更多