0

NSFetchedResultsController에서 내용을 수신하는 UICollectionView가 있습니다. 데이터는 여러 API- 엔드 포인트에서 제공됩니다 (서버 설계가 내 손안에 있음). 첫 번째 호출은 메타 데이터를 전달하며 각 엔트리 (> 10.000)보다 관련 데이터를 가져 오기위한 API 호출이 있어야합니다.CoreData 업데이트 중에 NSFetchedResultsController를 UICollectionView로 바꾸는 중 오류가 발생했습니다.

CollectionView 여러 속성에 대해 필터링 할 수 있으며, 각 필터는 사용자가 필터링을 변경 때, 나는 새와 현재의 FRC를 교체해야하는 다른 sectionNameKeyPath

이 필요합니다. CoreData이

*** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/UICollectionView.m:4422 
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete item 9 from section 0 which only contains 1 items before the update' 

으로 다음 응용 프로그램 충돌을 업데이트 때 그래서 충돌을 피할 수있는 방법은 무엇입니까?

편집 : NSFetchedResultsControllerDelegate에 대한 추가 코드 :

#pragma mark - NSFetchedResultsControllerDelegate 

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
{ 
    _objectChanges = [NSMutableDictionary dictionary]; 
    _sectionChanges = [NSMutableDictionary dictionary]; 
} 

- (void)controller:(NSFetchedResultsController *)controller 
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo 
      atIndex:(NSUInteger)sectionIndex 
    forChangeType:(NSFetchedResultsChangeType)type 
{ 


    if (type == NSFetchedResultsChangeInsert || type == NSFetchedResultsChangeDelete) { 
     NSMutableIndexSet *changeSet = _sectionChanges[@(type)]; 
     if (changeSet != nil) { 
      [changeSet addIndex:sectionIndex]; 
     } else { 
      _sectionChanges[@(type)] = [[NSMutableIndexSet alloc] initWithIndex:sectionIndex]; 
     } 
    } 

} 

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
     atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
    newIndexPath:(NSIndexPath *)newIndexPath 
{ 

    NSMutableArray *changeSet = _objectChanges[@(type)]; 
    if (changeSet == nil) { 
     changeSet = [[NSMutableArray alloc] init]; 
     _objectChanges[@(type)] = changeSet; 
    } 

    switch(type) { 
     case NSFetchedResultsChangeInsert: 
      [changeSet addObject:newIndexPath]; 
      break; 
     case NSFetchedResultsChangeDelete: 
      [changeSet addObject:indexPath]; 
      break; 
     case NSFetchedResultsChangeUpdate: 
      [changeSet addObject:indexPath]; 
      break; 
     case NSFetchedResultsChangeMove: 
      [changeSet addObject:@[indexPath, newIndexPath]]; 
      break; 
    } 

} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{ 

    NSMutableArray *moves = _objectChanges[@(NSFetchedResultsChangeMove)]; 
    if ([moves count] > 0) { 
     NSMutableArray *updatedMoves = [[NSMutableArray alloc] initWithCapacity:[moves count]]; 

     NSMutableIndexSet *insertSections = _sectionChanges[@(NSFetchedResultsChangeInsert)]; 
     NSMutableIndexSet *deleteSections = _sectionChanges[@(NSFetchedResultsChangeDelete)]; 
     for (NSArray *move in moves) { 
      NSIndexPath *oldIndexPath = move[0]; 
      NSIndexPath *newIndexPath = move[1]; 

      if ([deleteSections containsIndex:[oldIndexPath section]]) { 
       if (![insertSections containsIndex:[newIndexPath section]]) { 
        NSMutableArray *changeSet = _objectChanges[@(NSFetchedResultsChangeInsert)]; 
        if (changeSet == nil) { 
         changeSet = [[NSMutableArray alloc] initWithObjects:newIndexPath, nil]; 
         _objectChanges[@(NSFetchedResultsChangeInsert)] = changeSet; 
        } else { 
         [changeSet addObject:newIndexPath]; 
        } 
       } 
      } else if ([insertSections containsIndex:[newIndexPath section]]) { 
       NSMutableArray *changeSet = _objectChanges[@(NSFetchedResultsChangeDelete)]; 
       if (changeSet == nil) { 
        changeSet = [[NSMutableArray alloc] initWithObjects:oldIndexPath, nil]; 
        _objectChanges[@(NSFetchedResultsChangeDelete)] = changeSet; 
       } else { 
        [changeSet addObject:oldIndexPath]; 
       } 
      } else { 
       [updatedMoves addObject:move]; 
      } 
     } 

     if ([updatedMoves count] > 0) { 
      _objectChanges[@(NSFetchedResultsChangeMove)] = updatedMoves; 
     } else { 
      [_objectChanges removeObjectForKey:@(NSFetchedResultsChangeMove)]; 
     } 
    } 

    NSMutableArray *deletes = _objectChanges[@(NSFetchedResultsChangeDelete)]; 
    if ([deletes count] > 0) { 
     NSMutableIndexSet *deletedSections = _sectionChanges[@(NSFetchedResultsChangeDelete)]; 
     [deletes filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSIndexPath *evaluatedObject, NSDictionary *bindings) { 
      return ![deletedSections containsIndex:evaluatedObject.section]; 
     }]]; 
    } 

    NSMutableArray *inserts = _objectChanges[@(NSFetchedResultsChangeInsert)]; 
    if ([inserts count] > 0) { 
     NSMutableIndexSet *insertedSections = _sectionChanges[@(NSFetchedResultsChangeInsert)]; 
     [inserts filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSIndexPath *evaluatedObject, NSDictionary *bindings) { 
      return ![insertedSections containsIndex:evaluatedObject.section]; 
     }]]; 
    } 

    UICollectionView *collectionView = self.collectionView; 
    [collectionView performBatchUpdates:^{ 

     NSIndexSet *deletedSections = _sectionChanges[@(NSFetchedResultsChangeDelete)]; 
     if ([deletedSections count] > 0) { 
      [collectionView deleteSections:deletedSections]; 
     } 

     NSIndexSet *insertedSections = _sectionChanges[@(NSFetchedResultsChangeInsert)]; 
     if ([insertedSections count] > 0) { 
      [collectionView insertSections:insertedSections]; 
     } 

     NSArray *deletedItems = _objectChanges[@(NSFetchedResultsChangeDelete)]; 
     if ([deletedItems count] > 0) { 
      [collectionView deleteItemsAtIndexPaths:deletedItems]; 
     } 

     NSArray *insertedItems = _objectChanges[@(NSFetchedResultsChangeInsert)]; 
     if ([insertedItems count] > 0) { 
      [collectionView insertItemsAtIndexPaths:insertedItems]; 
     } 

     NSArray *reloadItems = _objectChanges[@(NSFetchedResultsChangeUpdate)]; 
     if ([reloadItems count] > 0) { 
      [collectionView reloadItemsAtIndexPaths:reloadItems]; 
     } 

     NSArray *moveItems = _objectChanges[@(NSFetchedResultsChangeMove)]; 
     for (NSArray *paths in moveItems) { 
      [collectionView moveItemAtIndexPath:paths[0] toIndexPath:paths[1]]; 
     } 
    } completion:^(BOOL finished) { 
     [[self refreshControl] endRefreshing]; 

    }]; 

    _objectChanges = nil; 
    _sectionChanges = nil; 

} 
+0

위임 콜백은 어떻게 처리합니까? 다시로드하거나 개별 변경 사항에 애니메이션을 적용하려고합니까? – Wain

+0

나는 모두 시도했습니다 performBatchUpdates를 사용합니다 : controllerDidChangeContent에서 그리고 블록에서 나는 itemChanges 및 sectionChanges에 대한 두 개의 mutableDictionaries를 처리합니다. 그러나 테스트를 위해 나는 또한 controllerDidChangeContent에서 간단한 "reloadData"를 시도했습니다. – MatzeLoCal

+0

은 변경 처리 코드와 변경 사항을 핵심 데이터로 저장하는 방법을 보여줍니다. – Wain

답변

0

하여 배치 코드가 영리 모양과 효율적인은 변화의 배치가 설득력 있는지 확인하는 것은 매우 어려운 일이지만. 어떤 섹션이 삽입되거나 삭제되거나 항목이 옮겨지면 매우 어려워서 다른 모든 색인 경로가 올바른지 확인하고 해당 항목을 참조하기가 어렵습니다.

섹션 삽입 또는 삭제가있는 경우 다시로드했습니다. 동시에 이동 된 항목과 다른 항목이있는 경우 다시로드합니다. 동일한 섹션에서 여러 번 삽입하고 삭제할 수있는 동시에 잘못된 데이터로 화면을 새로 고칠 수 있습니다.

컬렉션의 섹션 및 섹션 내용에 대한 복합 변경 사항을 신중하게 고려해야합니다.

FRC 변경에 대한 코드를 표시하지 않아도 관계가 없습니다. 전환 할 때 반드시 다시로드해야합니다.

+0

아, 변경 처리 코드를 잘못 이해 한 것 같습니다. FRC 변경 사항에 대해 여러 가지를 시도했습니다. 모든 FRC 변경이 발생하기 바로 전에 대리자를 제거하고 현재 FRC를 nil로 설정하고 CollectionView를 다시로드합니다.그런 다음 새로운 FRC – MatzeLoCal

+0

을 생성하면 변경 처리를위한 코드가 핵심 데이터로 저장되고 FRC 대리인 메서드로 저장됩니다. 두 가지 모두를 요청 했으므로 모두 좋습니다. FRC 변경에 따라 다른 FRC 인스턴스간에 전환하는 것을 의미합니다. 내 설명에 문제가 있습니다. – Wain

+0

내 문제는 심지어 batchUpdate 대신 reloadData가 충돌로 이어지는 것입니다. – MatzeLoCal