iOS 10中的UITableViewCell动画高度问题

cno*_*gr8 24 objective-c ios10

UITableViewCell当识别水龙头时,我的动画会高度显示它的高度.在iOS 9及更低版本中,这个动画很流畅,没有任何问题.在iOS 10测试版中,动画期间出现了一些不和谐的跳跃.有没有办法来解决这个问题?

这是代码的基本示例.

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

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

编辑:9/8/16

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

例如,如果我有一个约束到单元格底部的视图,它将跳转.解决方案将涉及使用动态常量对顶部的约束.或者想一下另一种方法来定位你的观点,然后再与底层相关.

小智 12

该解决方案iOS 10SwiftAutoLayout:

将此代码放入您的自定义中 UITableViewCell

override func layoutSubviews() {
    super.layoutSubviews()

    if #available(iOS 10, *) {
        UIView.animateWithDuration(0.3) { self.contentView.layoutIfNeeded() }
    }
}
Run Code Online (Sandbox Code Playgroud)

在Objective-C中:

- (void)layoutSubviews
{
    [super layoutSubviews];

    NSOperatingSystemVersion ios10 = (NSOperatingSystemVersion){10, 0, 0};
    if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10]) {
        [UIView animateWithDuration:0.3
                         animations:^{
                             [self.contentView layoutIfNeeded];
                         }];
    }
}
Run Code Online (Sandbox Code Playgroud)

UITableViewCell如果您在UITableViewDelegate下面配置了以下内容,这将为更改高度设置动画:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return selectedIndexes.contains(indexPath.row) ? Constants.expandedTableViewCellHeight : Constants.collapsedTableViewCellHeight
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
    tableView.beginUpdates()
    if let index = selectedIndexes.indexOf(indexPath.row) {
        selectedIndexes.removeAtIndex(index)
    } else {
        selectedIndexes.append(indexPath.row)
    }
    tableView.endUpdates()
}
Run Code Online (Sandbox Code Playgroud)


cno*_*gr8 8

改善方案:

问题是当UITableView改变单元格的高度时,最有可能来自-beginUpdates-endUpdates.到iOS 10之前对细胞的动画会发生两者的sizeorigin.现在,在iOS 10 GM中,单元格将立即更改为新高度,然后将动画显示正确的偏移量.

约束条件非常简单.创建一个引导约束,它将更新它的高度并使其他视图需要约束到单元格的底部,现在约束到本指南.

- (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;
}
Run Code Online (Sandbox Code Playgroud)

然后在需要时更新指南高度.

- (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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,放入更新指南-setFrame:可能不是最佳选择.截至目前,我只构建了此演示代码来创建解决方案.一旦我用最终解决方案更新了我的代码,如果我找到一个更好的地方放置它我将编辑.

原答案:

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

我的解决方案涉及到层的下降CAAnimation.这将检测高度的变化并自动设置动画,就像使用CALayer未连接的a一样UIView.

第一步是调整图层检测到更改时发生的情况.此代码必须位于视图的子类中.这种观点必须是你的子视图tableViewCell.contentView.在代码中,我们检查视图的图层的actions属性是否具有动画的键.如果不只是打电话给超级.

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

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

- (void)didMoveToWindow {
    [super didMoveToWindow];

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

就是这样!不需要应用animation.duration表格视图-beginUpdates-endUpdates覆盖它.一般来说,如果你使用这个技巧作为一种无障碍的动画应用方式,你会想要添加一个animation.duration,也许还有一个animation.timingFunction.


Mar*_*kus 6

我不使用自动布局,而是从UITableViewCell派生一个类,并使用其"layoutSubviews"以编程方式设置子视图的帧.所以我试着修改cnotethegr8的答案以满足我的需求,我想出了以下内容.

重新实现单元格的"setFrame",将单元格的内容高度设置为单元格高度,并触发动画块中子视图的重新布局:

@interface MyTableViewCell : UITableViewCell
...
@end

@implementation MyTableViewCell

...

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

    if (self.window)
    {
        [UIView animateWithDuration:0.3 animations:^{
            self.contentView.frame = CGRectMake(
                                        self.contentView.frame.origin.x,
                                        self.contentView.frame.origin.y,
                                        self.contentView.frame.size.width,
                                        frame.size.height);
            [self setNeedsLayout];
            [self layoutIfNeeded];
        }];
    }
}

...

@end
Run Code Online (Sandbox Code Playgroud)

这就是诀窍:)