UITableView 在同一批次更新中重新加载和移动行导致“尝试为单元格创建两个动画”
Posted
技术标签:
【中文标题】UITableView 在同一批次更新中重新加载和移动行导致“尝试为单元格创建两个动画”【英文标题】:UITableView reload and move rows in same batch update results in 'Attempt to create two animations for cell' 【发布时间】:2015-01-14 12:10:12 【问题描述】:这是一个简单的例子(基本上是由“Master-Detail”Xcode 模板制作的)。
我正在尝试在 UITableView 的批量更新中移动一行(“B”)并重新加载另一行(“A1”->“A2”)。
- (void)awakeFromNib
[super awakeFromNib];
self.objects = @[@"B", @"A1"];
- (IBAction)boom
self.objects = @[@"A2", @"B"];
NSIndexPath *path0 = [NSIndexPath indexPathForRow:0 inSection:0];
NSIndexPath *path1 = [NSIndexPath indexPathForRow:1 inSection:0];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[path1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
[self.tableView endUpdates];
这会导致以下异常:
2015-01-14 17:58:51.290 batchtest[34004:806671] *** Assertion failure in -[_UITableViewUpdateSupport _setupAnimationsForNewlyInsertedCells], /SourceCache/UIKit_Sim/UIKit-3318.16.14/UITableViewSupport.m:1216
2015-01-14 17:58:51.335 batchtest[34004:806671] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempt to create two animations for cell'
*** First throw call stack:
(
0 CoreFoundation 0x00946946 __exceptionPreprocess + 182
1 libobjc.A.dylib 0x005cfa97 objc_exception_throw + 44
2 CoreFoundation 0x009467da +[NSException raise:format:arguments:] + 138
3 Foundation 0x00243810 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 118
4 UIKit 0x010b18e5 -[_UITableViewUpdateSupport(Private) _setupAnimationsForNewlyInsertedCells] + 8447
5 UIKit 0x010bb422 -[_UITableViewUpdateSupport _setupAnimations] + 207
6 UIKit 0x00df8bc1 -[UITableView _updateWithItems:updateSupport:] + 2089
7 UIKit 0x00df24a8 -[UITableView _endCellAnimationsWithContext:] + 13867
8 UIKit 0x00e07b6f -[UITableView endUpdatesWithContext:] + 51
9 UIKit 0x00e07b9d -[UITableView endUpdates] + 41
当我尝试将@[path0]
传递给reloadRowsAtIndexPaths
而不是@[path1]
时,也会发生同样的事情。
对我来说,这看起来像是一个 ios 错误。有办法解决吗?我做错了吗?
【问题讨论】:
这是 Apple 工程部门对此问题的回答:***.com/a/9642438/2128900。似乎您必须使用一个丑陋的解决方法:“只需进入单元格并更改内容”。 谢谢,米哈乌。问题在于重新加载正在移动的同一行(顺便说一句,这也是有道理的)。我正在尝试重新加载 另一个 单元格。无论如何,我会考虑一些通用的方法来直接更新单元格而不是调用“重新加载”(显然我真正在做的并不是这个带有两个单元格的示例)。 请参考:***.com/questions/11372884/… 【参考方案1】:在我看来,这个例外没有任何问题。 reloadRowsAtIndexPaths:withRowAnimation:
为正在重新加载的行创建动画,moveRowAtIndexPath:toIndexPath:
也创建动画。很明显,UITableView 无法同时处理 2 个动画并抛出异常。
我想将UITableViewRowAnimationNone
传递给reloadRowsAtIndexPaths:withRowAnimation:
可以解决问题,但不确定;不过,值得一试。
我会提出这样的解决方法:
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
dispatch_async(dispatch_get_main_queue(), ^
self.objects = @[@"A2", @"B"];
[self.tableView reloadRowsAtIndexPaths:@[path1]
withRowAnimation:UITableViewRowAnimationAutomatic];
);
或者这个:
[CATransaction begin];
[CATransaction setCompletionBlock:^
self.objects = @[@"A2", @"B"];
[self.tableView reloadRowsAtIndexPaths:@[path1]
withRowAnimation:UITableViewRowAnimationAutomatic];
];
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
[CATransaction commit];
或者这个:
[UIView animateWithDuration:0.2 animations:^
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
completion:^(BOOL finished)
self.objects = @[@"A2", @"B"];
[self.tableView reloadRowsAtIndexPaths:@[path1]
withRowAnimation:UITableViewRowAnimationAutomatic];
];
【讨论】:
UITableView 可以在批量更新中处理多个动画——这就是批量更新的用途。它抱怨的是单个 cell 有两个动画。但在我看来,重新加载动画会转到 [最初] 第二个单元格,而移动动画会转到 [最初] 第一个单元格。 我猜所有的动画都是同时创建的,所以你会同时为path1
制作几个动画
UITableViewRowAnimationNone 没有帮助。此外,我不想引入任何异步,因为如果在两者之间发生另一个更新,它将中断。但是,首先“移动”,然后在单独的批次中同步“重新加载”,确实有帮助。但我看不出这背后有任何原因,特别是因为“重新加载,然后移动”无法正常工作。
path1
in "move" 是 "B" 单元格将移动到的位置,它与 最初 的 cell 不同在path1
。
我不认为 CATransaction 与 dispatch_async 有什么不同,比如它不会等待上一个动画结束或任何事情,因为它不是在那里创建的,而是在 endUpdates 中创建的。跨度>
【参考方案2】:
我解决了这个问题,只是取消了[tableView beginUpdates]
和[tableView endUpdates]
。
顺便说一句,iOS 8.4 和 9.x 解决了这个问题,但是,iOS 8.1 不行。
【讨论】:
【参考方案3】:就像@Dawn Song 说的:在iOS 8.4 up,这个问题是不会发生的,但我猜只有8.1 down(还没有测试8.0及down)
在 iOS8.1 中,也许你应该一个一个地更新单元格。这对我有用: 你的代码:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[path1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
[self.tableView endUpdates];
尝试更改为:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[path1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:path0 toIndexPath:path1];
[self.tableView endUpdates];
但请注意:您的 path1
应该只有 1 个 NSIndexPath
【讨论】:
以上是关于UITableView 在同一批次更新中重新加载和移动行导致“尝试为单元格创建两个动画”的主要内容,如果未能解决你的问题,请参考以下文章
NSMutableArray 未更新且 UITableView 未重新加载
在 iOS 中重新加载特定的 UITableView 单元格
重新加载行以更新 UITableView 中的单元格高度时出现问题