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:

  1. UIScrollView: detect if we are at bottom
  2. Debugging Core Data
  3. Core Data Notes from iPhone Tech Talk
  4. Crash Reports on the iPhone
  5. setScrollsToTop broken?

1 Response to Core Animation Crash: Attempt to create two animations for cell

Avatar

bob

January 12th, 2011 at 12:10 am

isIndexPathInSet:(NSIndexPath *)aPath

should be implemented as

return [updatedRows containsObject:aPath];

Comment Form

top

Switch to our mobile site