iOS 10中的UITableViewCell动画高度问题 [英] UITableViewCell animate height issue in iOS 10

查看:93
本文介绍了iOS 10中的UITableViewCell动画高度问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的UITableViewCell会在识别出水龙头时对其高度进行动画处理.在iOS 9及以下版本中,此动画很流畅,并且没有问题.在iOS 10 Beta中,动画过程中出现了令人讨厌的跳跃.有办法解决这个问题吗?

My UITableViewCell will animate it's height when recognizing a tap. In iOS 9 and below this animation is smooth and works without issues. In iOS 10 beta there's a jarring jump during the animation. Is there a way to fix this?

这是代码的基本示例.

- (void)cellTapped {
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return self.shouldExpandCell ? 200.0f : 100.0f;
}

9/8/16

GM中仍然存在该问题.经过更多调试后,我发现问题与单元格立即跳至新高度有关,然后将使相关单元格偏移动画.这意味着依赖于单元格底部的任何基于CGRect的动画都将不起作用.

The issue still exists in the GM. After more debugging I have discovered the issue is related to the cell immediately jumping to the new height and then will animate the relevant cells offset. This means any CGRect based animation which is dependent on the cells bottom will not work.

例如,如果我的视图局限于单元格底部,它将跳转.该解决方案将涉及到一个具有动态常数的顶部约束.或想出另一种方式来定位您的视图,然后再将其与底部相关.

For instance if I have a view constrained to the cells bottom, it will jump. The solution would involve a constraint to the top with a dynamic constant. Or think of another way to position your views other then related to the bottom.

推荐答案

更好的解决方案:

问题是当UITableView更改单元格的高度时,很可能是-beginUpdates-endUpdates.在iOS 10之前,sizeorigin都将在单元上进行动画处理.现在,在iOS 10 GM中,单元格将立即更改为新的高度,然后将其设置为正确的偏移量.

The issue is when the UITableView changes the height of a cell, most likely from -beginUpdates and -endUpdates. Prior to iOS 10 an animation on the cell would take place for both the size and the origin. Now, in iOS 10 GM, the cell will immediately change to the new height and then will animate to the correct offset.

该解决方案非常简单,有约束条件.创建一个导向约束,以更新其高度,并将其他视图需要约束到单元格的底部,现在已约束到该向导.

The solution is pretty simple with constraints. Create a guide constraint which will update it's height and have the other views which need to be constrained to the bottom of the cell, now constrained to this guide.

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        UIView *heightGuide = [[UIView alloc] init];
        heightGuide.translatesAutoresizingMaskIntoConstraints = NO;
        [self.contentView addSubview:heightGuide];
        [heightGuide addConstraint:({
            self.heightGuideConstraint = [NSLayoutConstraint constraintWithItem:heightGuide attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0.0f];
        })];
        [self.contentView addConstraint:({
            [NSLayoutConstraint constraintWithItem:heightGuide attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f];
        })];

        UIView *anotherView = [[UIView alloc] init];
        anotherView.translatesAutoresizingMaskIntoConstraints = NO;
        anotherView.backgroundColor = [UIColor redColor];
        [self.contentView addSubview:anotherView];
        [anotherView addConstraint:({
            [NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:20.0f];
        })];
        [self.contentView addConstraint:({
            [NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f];
        })];
        [self.contentView addConstraint:({
            // This is our constraint that used to be attached to self.contentView
            [NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:heightGuide attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
        })];
        [self.contentView addConstraint:({
            [NSLayoutConstraint constraintWithItem:anotherView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f];
        })];
    }
    return self;
}

然后在需要时更新导向高度.

Then update the guides height when needed.

- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];

    if (self.window) {
        [UIView animateWithDuration:0.3 animations:^{
            self.heightGuideConstraint.constant = frame.size.height;
            [self.contentView layoutIfNeeded];
        }];

    } else {
        self.heightGuideConstraint.constant = frame.size.height;
    }
}

请注意,将更新指南放入-setFrame:可能不是最佳位置.到目前为止,我只构建了此演示代码来创建解决方案.在完成使用最终解决方案的代码更新后,如果我找到放置它的更好位置,我将进行编辑.

Note that putting the update guide in -setFrame: might not be the best place. As of now I have only built this demo code to create a solution. Once I finish updating my code with the final solution, if I find a better place to put it I will edit.

原始答案:

随着iOS 10 Beta即将完成,希望此问题将在下一版本中得到解决.还有一个开放的错误报告.

With the iOS 10 beta nearing completion, hopefully this issue will be resolved in the next release. There's also an open bug report.

  • https://openradar.appspot.com/27679031
  • https://forums.developer.apple.com/thread/53602

我的解决方案涉及放置到图层的CAAnimation.这将检测高度的变化并自动进行动画处理,就像使用未链接到UIViewCALayer.

My solution involves dropping to the layer's CAAnimation. This will detect the change in height and automatically animate, just like using a CALayer unlinked to a UIView.

第一步是调整当图层检测到更改时发生的情况.此代码必须在视图的子类中.该视图必须是您的tableViewCell.contentView的子视图.在代码中,我们检查视图的图层的actions属性是否具有动画的关键点.如果不是,那就叫超级.

The first step is to adjust what happens when the layer detects a change. This code has to be in the subclass of your view. That view has to be a subview of your tableViewCell.contentView. In the code, we check if the view's layer's actions property has the key of our animation. If not just call super.

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
    return [layer.actions objectForKey:event] ?: [super actionForLayer:layer forKey:event];
}

接下来,您要将动画添加到actions属性.您可能会在视图位于窗口上并布局后,发现最好应用此代码.事先应用它可能会导致视图移动到窗口时产生动画.

Next you want to add the animation to the actions property. You might find this code is best applied after the view is on the window and laid out. Applying it beforehand might lead to an animation as the view moves to the window.

- (void)didMoveToWindow {
    [super didMoveToWindow];

    if (self.window) {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
        self.layer.actions = @{animation.keyPath:animation};
    }
}

就是这样!无需应用animation.duration,因为表视图的-beginUpdates-endUpdates会覆盖它.通常,如果您将此技巧用作应用动画的便捷方式,则需要添加animation.duration,也许还可以添加animation.timingFunction.

And that's it! No need to apply an animation.duration since the table view's -beginUpdates and -endUpdates overrides it. In general if you use this trick as a hassle-free way of applying animations, you will want to add an animation.duration and maybe also an animation.timingFunction.

这篇关于iOS 10中的UITableViewCell动画高度问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆