Core Animation Crash: Attempt to create two animations for cell
1
Mar
2010
Google’s not very helpful on this error. NSInternalInconsistencyException ‘Attempt to create two animations for cell’
After banging my head against the wall several times, I got the solution – and how simple it is! I’ll post the full code here.
In short, remember all your insert/update/delete operations and make sure only one animation per cell is called.
Remember this is three20 code, and you’ll need the willUpdateFor- functions in the DataSource. This overrides code from three20′s internal sources.
Update: changed dictionary to set.
// a beginUpdate - endUpdate block is only allowed if the total sum of elements is correct after the update
// so we need to check if view is visible, or else just update on endUpdates.
- (void)beginUpdates {
if (_isViewAppearing && _flags.isShowingModel) {
[_tableView beginUpdates];
updatedRows = [[NSMutableSet set] retain];
}
}
- (void)endUpdates {
if (_isViewAppearing && _flags.isShowingModel) {
[_tableView endUpdates];
RELEASE(updatedRows);
}else {
[_tableView reloadData];
}
}
- (BOOL)isIndexPathInSet:(NSIndexPath *)aPath {
if (updatedRows) {
for(NSIndexPath *path in updatedRows) {
if ([path compare:aPath] == NSOrderedSame) {
return YES;
}
}
}
return NO;
}
- (void)model:(id<TTModel>)model didUpdateObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
if (model == _model) {
if (_isViewAppearing && _flags.isShowingModel) {
if ([_dataSource respondsToSelector:@selector(tableView:willUpdateObject:atIndexPath:)]) {
NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willUpdateObject:object
atIndexPath:indexPath];
if (newIndexPath) {
if (newIndexPath.length == 1) {
MRDINFO(@"UPDATING SECTION AT %@", newIndexPath);
NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
[_tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationTop];
} else if (newIndexPath.length == 2) {
MRDINFO(@"UPDATING ROW AT %@", newIndexPath);
if (![self isIndexPathInSet:newIndexPath]) {
[updatedRows addObject:newIndexPath];
[_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}else {
// fixes NSInternalInconsistencyException 'Attempt to create two animations for cell'
MRDINFO(@"prevented multiple updates for %@", newIndexPath);
}
}
[self invalidateView];
} else {
[_tableView reloadData];
}
}
} else {
[self refresh];
}
}
}
- (void)model:(id<TTModel>)model didInsertObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
if (model == _model) {
if (_isViewAppearing && _flags.isShowingModel) {
if ([_dataSource respondsToSelector:@selector(tableView:willInsertObject:atIndexPath:)]) {
NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willInsertObject:object
atIndexPath:indexPath];
if (newIndexPath) {
if (newIndexPath.length == 1) {
MRDINFO(@"INSERTING SECTION AT %@", newIndexPath);
NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
[_tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationTop];
} else if (newIndexPath.length == 2) {
MRDINFO(@"INSERTING ROW AT %@", newIndexPath);
if (![self isIndexPathInSet:newIndexPath]) {
[updatedRows addObject:newIndexPath];
[_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationTop];
}else {
MRDINFO(@"prevented multiple updates for %@", newIndexPath);
}
// crashes sometimes with index out of bounds. not a good idea at all.
//[_tableView scrollToRowAtIndexPath:newIndexPath
// atScrollPosition:UITableViewScrollPositionNone animated:NO];
}
[self invalidateView];
} else {
[_tableView reloadData];
}
}
} else {
[self refresh];
}
}
}
- (void)model:(id<TTModel>)model didDeleteObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
if (model == _model) {
if (_isViewAppearing && _flags.isShowingModel) {
if ([_dataSource respondsToSelector:@selector(tableView:willRemoveObject:atIndexPath:)]) {
NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willRemoveObject:object
atIndexPath:indexPath];
if (newIndexPath) {
if (newIndexPath.length == 1) {
MRDINFO(@"DELETING SECTION AT %@", newIndexPath);
NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationLeft];
} else if (newIndexPath.length == 2) {
MRDINFO(@"DELETING ROW AT %@", newIndexPath);
if (![self isIndexPathInSet:newIndexPath]) {
[updatedRows addObject:newIndexPath];
[_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationLeft];
}else {
MRDINFO(@"prevented multiple updates for %@", newIndexPath);
}
}
[self invalidateView];
} else {
[_tableView reloadData];
}
}
} else {
[self refresh];
}
}
}
Related posts:
- UIScrollView: detect if we are at bottom
- Debugging Core Data
- Core Data Notes from iPhone Tech Talk
- Tweetie like swipe menu for iPhone apps
- Crash Reports on the iPhone