Objective c iOS 10中的UITableViewCell动画高度问题
我的Objective c iOS 10中的UITableViewCell动画高度问题,objective-c,ios10,Objective C,Ios10,我的UITableViewCell将在识别轻触时设置其高度的动画。在iOS 9及以下版本中,此动画平滑且工作正常。在iOS 10 beta版中,动画过程中会出现不和谐的跳跃。有办法解决这个问题吗 下面是代码的一个基本示例 - (void)cellTapped { [self.tableView beginUpdates]; [self.tableView endUpdates]; } - (CGFloat)tableView:(UITableView *)tableView h
UITableViewCell
将在识别轻触时设置其高度的动画。在iOS 9及以下版本中,此动画平滑且工作正常。在iOS 10 beta版中,动画过程中会出现不和谐的跳跃。有办法解决这个问题吗
下面是代码的一个基本示例
- (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
的动画都无法工作
例如,如果将视图约束到单元格底部,它将跳转。该解决方案将涉及使用动态常数对顶部的约束。或者考虑另一种方法来定位视图,而不是与底部相关。更好的解决方案: 问题是当
UITableView
更改单元格高度时,很可能是从-beginUpdates
和-endUpdates
更改单元格高度。在iOS 10之前,大小
和原点
都会在单元格上产生动画。现在,在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;
}
然后在需要时更新导轨高度
- (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:
中可能不是最佳位置。到目前为止,我只构建了这个演示代码来创建一个解决方案。一旦我用最终的解决方案更新完代码,如果我找到一个更好的地方放置它,我将进行编辑
原始答案:
随着iOS 10测试版接近完成,这个问题有望在下一版本中得到解决。还有一个开放的bug报告
CAAnimation
。这将检测高度的变化并自动设置动画,就像使用未链接到ui视图的CALayer
一样
第一步是调整图层检测到变化时发生的情况。此代码必须位于视图的子类中。该视图必须是tableViewCell.contentView
的子视图。在代码中,我们检查视图层的actions属性是否具有动画的关键点。如果不是,就打电话给super
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
return [layer.actions objectForKey:event] ?: [super actionForLayer:layer forKey:event];
}
就这样!无需应用动画。持续时间
,因为表视图的-beginUpdates
和-endUpdates
会覆盖它。通常,如果您使用此技巧作为应用动画的无障碍方式,您将需要添加一个动画.duration
,还可能添加一个动画.timingFunction
我不使用自动布局,而是从UITableViewCell派生一个类,并使用其“LayoutSubView”以编程方式设置子视图的帧。因此,我试图修改CNOTETEGR8的答案以满足我的需要,并得出以下结论
重新实现单元的“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
这就成功了:)在Swift
中使用AutoLayout
的ios10
解决方案:
将此代码放入您的自定义UITableViewCell
override func layoutSubviews() {
super.layoutSubviews()
if #available(iOS 10, *) {
UIView.animateWithDuration(0.3) { self.contentView.layoutIfNeeded() }
}
}
在目标C中:
- (void)layoutSubviews
{
[super layoutSubviews];
NSOperatingSystemVersion ios10 = (NSOperatingSystemVersion){10, 0, 0};
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10]) {
[UIView animateWithDuration:0.3
animations:^{
[self.contentView layoutIfNeeded];
}];
}
}
如果您已将UITableViewDelegate
配置为如下所示,则这将设置UITableViewCell
的动画以更改高度:
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()
}
在iOS 10上遇到同样的问题(在iOS 9上一切都很好),解决方案是通过UITableViewDelegate方法实现提供实际的EstimateDrowehight和heightForRow
单元格的子视图使用AutoLayout定位,因此我使用UITableViewAutomaticDimension进行高度计算(您可以尝试将其设置为tableView的rowHeight属性)
因此,对于使用布局约束的UITableViewCell
和使用自动单元格高度的UITableView
(UITableViewAutomaticDimension
),我遇到了这个问题。所以我不确定这个解决方案中有多少对你有用,但我把它放在这里,因为它是密切相关的
主要实现是降低导致高度变化的约束的优先级:
self.collapsedConstraint = self.expandingContainer.topAnchor.constraintEqualToAnchor(self.bottomAnchor).priority(UILayoutPriorityDefaultLow)
self.expandedConstraint = self.expandingContainer.bottomAnchor.constraintEqualToAnchor(self.bottomAnchor).priority(UILayoutPriorityDefaultLow)
self.expandedConstraint?.active = self.expanded
self.collapsedConstraint?.active = !self.expanded
其次,我制作tableview动画的方式非常具体:
UIView.animateWithDuration(0.3) {
let tableViewCell = tableView.cellForRowAtIndexPath(viewModel.index) as! MyTableViewCell
tableViewCell.toggleExpandedConstraints()
tableView.beginUpdates()
tableView.endUpdates()
tableViewCell.layoutIfNeeded()
}
请注意,layoutifneed()
必须在beginUpdates
/endUpdates
我认为最后一部分可能会对您有所帮助……由于声誉问题,我无法发表评论,但sam的解决方案在IOS 10上对我有效
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
EWGDownloadTableViewCell *cell = (EWGDownloadTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
if (self.expandedRow == indexPath.row) {
self.expandedRow = -1;
[UIView animateWithDuration:0.3 animations:^{
cell.constraintToAnimate.constant = 51;
[tableView beginUpdates];
[tableView endUpdates];
[cell layoutIfNeeded];
} completion:nil];
} else {
self.expandedRow = indexPath.row;
[UIView animateWithDuration:0.3 animations:^{
cell.constraintToAnimate.constant = 100;
[tableView beginUpdates];
[tableView endUpdates];
[cell layoutIfNeeded];
} completion:nil];
} }
其中constraintToAnimate是添加到单元格内容视图的子视图上的高度约束。Swift 4^
当我在表的底部尝试展开和折叠tableView中的某些行时,出现了跳转问题。我首先尝试了这个解决方案:
var cellHeights: [IndexPath: CGFloat] = [:]
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? UITableView.automaticDimension
}
但是,当我尝试展开行,向上滚动,然后突然折叠行时,问题再次出现。所以我试了一下:
let savedContentOffset = contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(savedContentOffset, animated: false)
这不知怎么解决了它。尝试这两种解决方案,看看什么适合你。希望这能有所帮助。哇,真是先进的东西。它对我起到了一半的作用:我的视图首先跳到不同的位置,但大小设置正确。看起来我的“边界”和“中心”发生了变化,后者没有设置动画。我尝试使用CAAnimationGroup进行self.layer.actions,但没有成功。。。你知道怎么处理吗?还有一个问题:那么这段代码需要添加到contentView的每个子视图中,这些子视图应该被设置动画?我无法获得像iOS9中那样的精确动画,iOS10中有很多错误,因此,由于此解决方案显著减少了跳跃,因此10仍然是
var cellHeights: [IndexPath: CGFloat] = [:]
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? UITableView.automaticDimension
}
let savedContentOffset = contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(savedContentOffset, animated: false)