我正在使用包含在 beginUpdates/endUpdates
中的 insertRowsAtIndexPaths/deleteRowsAtIndexPaths
插入/删除表格单元格。调整 rowHeight 时,我也在使用 beginUpdates/endUpdates
。默认情况下,所有这些操作都是动画的。
使用 beginUpdates/endUpdates
时如何检测动画已结束?
-scrollToRowAtIndexPath:atScrollPosition:animated:
。
那这个呢?
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// animation has finished
}];
[tableView beginUpdates];
// do some work
[tableView endUpdates];
[CATransaction commit];
这是因为 tableView 动画在内部使用 CALayer
动画。也就是说,他们将动画添加到任何打开的 CATransaction
。如果不存在 open CATransaction
(正常情况),则隐式开始一个,在当前运行循环结束时结束。但是,如果您自己开始一个,就像这里所做的那样,那么它将使用那个。
斯威夫特版本
CATransaction.begin()
CATransaction.setCompletionBlock({
do.something()
})
tableView.beginUpdates()
tableView.endUpdates()
CATransaction.commit()
如果您的目标是 iOS 11 及更高版本,则应改用 UITableView.performBatchUpdates(_:completion:)
:
tableView.performBatchUpdates({
// delete some cells
// insert some cells
}, completion: { finished in
// animation complete
})
一种可能的解决方案是从调用 endUpdates
的 UITableView 继承并覆盖其 setContentSizeMethod
,因为 UITableView 会调整其内容大小以匹配添加或删除的行。这种方法也应该适用于 reloadData
。
为确保仅在调用 endUpdates
后才发送通知,还可以覆盖 endUpdates
并在那里设置一个标志。
// somewhere in header
@private BOOL endUpdatesWasCalled_;
-------------------
// in implementation file
- (void)endUpdates {
[super endUpdates];
endUpdatesWasCalled_ = YES;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
if (endUpdatesWasCalled_) {
[self notifyEndUpdatesFinished];
endUpdatesWasCalled_ = NO;
}
}
您可以将您的操作包含在 UIView 动画块中,如下所示:
- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
[UIView animateWithDuration:0.0 animations:^{
[tableView beginUpdates];
if (operation)
operation();
[tableView endUpdates];
} completion:^(BOOL finished) {
if (completion)
completion(finished);
}];
}
归功于 https://stackoverflow.com/a/12905114/634940。
endUpdates
之后但在动画完成之前调用了完成块。
还没有找到好的解决方案(缺少子类化 UITableView)。我现在决定使用 performSelector:withObject:afterDelay:
。不理想,但可以完成工作。
更新:看起来我可以为此目的使用 scrollViewDidEndScrollingAnimation:
(这是特定于我的实现,请参阅评论)。
scrollViewDidEndScrollingAnimation
仅在响应 setContentOffset
和 scrollRectToVisible
时被调用
您可以像这样使用 tableView:willDisplayCell:forRowAtIndexPath:
:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"tableView willDisplay Cell");
cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}
但是,当表格中已经存在的单元格从屏幕外移到屏幕上时,这也会被调用,因此它可能不是您要查找的内容。我只是查看了所有 UITableView
和 UIScrollView
委托方法,并且在插入单元格动画之后似乎没有任何事情要处理。
为什么不直接在 endUpdates
之后动画结束时调用你想调用的方法呢?
- (void)setDownloadedImage:(NSMutableDictionary *)d {
NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
[indexPathDelayed addObject:indexPath];
if (!([table isDragging] || [table isDecelerating])) {
[table beginUpdates];
[table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
[table endUpdates];
// --> Call Method Here <--
loadingView.hidden = YES;
[indexPathDelayed removeAllObjects];
}
}
willDisplayCell
会起作用。我需要它在动画之后发生,因为我需要在一切都解决之后进行视觉更新。
不定期副业成功案例分享
beginUpdates
和endUpdates
之前设置了completionBlock
?[CATransaction commit]
应该在[tableView endUpdates]
之后而不是之前调用。