2010-02-02 68 views
2

The documentation says:如何從NSFetchedResultsControllerDelegate實現延遲/批處理表視圖更新?

您應仔細考慮是否要爲每一個做出改變來更新表視圖。如果同時進行大量修改 - 例如,如果您正在從後臺線程/.../中讀取數據,則只需實現controllerDidChangeContent :(在處理完所有待處理更改後發送給委託)到重新加載表格視圖。

這正是我在做什麼:我在處理具有不同ManagedObjectContext後臺線程傳入的變化,以及合併結果與mergeChangesFromContextDidSaveNotification主線程MOC :.到現在爲止還挺好。

我選擇不執行controller:didChangeObject:...,而是想要執行文檔建議的批處理更新。

問題/問題:文檔沒有詳細說明如何實際實現批量更新?我應該只是在controllerDidChangeContent中調用[tableview reloadData]:還是有一種不太乾擾的方式可以讓我完全重新加載?

想到我有:我可以注意到mergeChangesFrom ...包含更改的對象的通知,找出它們的索引路徑,並調用tableview:ReloadRowsAtIndexPaths:爲他們。但是有沒有權威的信息,建議或例子?或者只是[tableview reloadData]? (另:控制器:didChangeObject:...當它接收到一組批處理更新時開始表現得非常不正常,即使同樣的更新代碼[我現在放在後臺線程中]在它運行之前很好主線程,但當然鎖定用戶界面。)

回答

1

我只需撥打reloadDatacontrollerDidChangeContent:

對於動畫表中各個更改,蘋果公司的鍋爐板代碼(iOS版SDK版本4.3.x)看起來是這樣的:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
{ 
    [self.tableView beginUpdates]; 
} 

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo 
      atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 
{ 
    switch(type) 
    { 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
     atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
     newIndexPath:(NSIndexPath *)newIndexPath 
{ 
    UITableView *tableView = self.tableView; 

    switch(type) 
    { 

     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{ 
    [self.tableView endUpdates]; 
} 
+0

嘆息......如果這就是它的原因:( – Jaanus 2010-02-02 10:04:30

+0

)reloadData是一個很好的解決方案,當被改變的對象數量很大的時候,但是你怎麼知道呢?在我的情況下,我想能夠知道有多少變化已被檢測到,以便我可以決定是否需要完整的reloadData或流暢的動畫更新 – 2011-09-09 14:15:26

+0

@DirtyHenry我已經添加了用於處理動畫更新的鍋爐板代碼,我想NSFetchedResultsController會在調用這些方法之後你可以在你的管理對象上下文中調用'save',因此,在調用'save'之前,你應該看到有多少記錄被改變了,如果超過了你的閾值,只需在controllerDidChangeContent中調用'reloadData'而不是'endUpdates'返回其他委託方法的頂部)。 – gerry3 2011-09-23 22:13:11

0

你可以注意到,在您的數據源的最後一個索引路徑&然後做到這一點 -

NSIndexPath *indexPath  = nil; 
    NSMutableArray *newResults = [[NSMutableArray alloc] init]; 
    if(gLastResultIndex < [self.dataSource count]) 
    { 
     for(int i=(gLastResultIndex); i<[self.dataSource count]; i++) 
     { 
      indexPath = [NSIndexPath indexPathForRow:i inSection:0]; 
      [newResults addObject:indexPath]; 
     } 
     [self.table beginUpdates]; 
     [self.table insertRowsAtIndexPaths:newResults withRowAnimation:UITableViewRowAnimationNone]; 
     [self.table endUpdates]; 
    } 
    [newResults release]; 

這是有選擇地添加新行到您現有的UITableView。重新加載表將重新加載您可能不需要的表中的所有單元格。我只在整個數據源發生變化時才使用重載表...

1

我相信這是你要找的東西:

與其說

  • deleteSections
  • insertSections
  • reloadSections
  • deleteRowsAtIndexPaths
  • insertRowsAtIndexPaths
  • reloadRowsAtIndexPaths

爲改變在controller:didChangeObject:atIndexPath:forChangeType:newIndexPath進來,收集性能indexPaths和路段。然後在controllerDidChangeContent:中執行該批次。

編號:http://www.fruitstandsoftware.com/blog/2013/02/19/uitableview-and-nsfetchedresultscontroller-updates-done-right/

編輯

這裏有一個稍微不同的,更做作,但也更緊湊,版本中的鏈接中提到的方法:

@property (nonatomic, strong) NSMutableArray *sectionChanges; 
@property (nonatomic, strong) NSMutableArray *objectChanges; 

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

} 

- (void)controller:(NSFetchedResultsController *)controller 
    didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo 
      atIndex:(NSUInteger)sectionIndex 
    forChangeType:(NSFetchedResultsChangeType)type 
{ 
    NSMutableDictionary *sectionChange = [NSMutableDictionary new]; 

    switch(type) 
    { 
     case NSFetchedResultsChangeInsert: 
     case NSFetchedResultsChangeDelete: 
     case NSFetchedResultsChangeUpdate: 
      sectionChange[@(type)] = @(sectionIndex); 
      break; 
    } 

    [self.sectionChanges addObject:sectionChange]; 
} 

- (void)controller:(NSFetchedResultsController *)controller 
    didChangeObject:(id)anObject 
     atIndexPath:(NSIndexPath *)indexPath 
    forChangeType:(NSFetchedResultsChangeType)type 
     newIndexPath:(NSIndexPath *)newIndexPath 
{ 
    NSMutableDictionary *objectChange = [NSMutableDictionary new]; 

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

    [self.objectChanges addObject:objectChange];   
} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{ 
    UITableView *tableView = self.tableView; 

    NSUInteger totalChanges = self.sectionChanges.count + self.objectChanges.count; 

    if (totalChanges > 0) 
    { 
     [tableView beginUpdates]; 

     if (self.sectionChanges.count > 0) 
     { 
      for (NSDictionary *change in self.sectionChanges) 
      { 
       [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) { 

        NSFetchedResultsChangeType type = [key unsignedIntegerValue]; 

        switch (type) 
        { 
         case NSFetchedResultsChangeInsert: 
          [tableView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
         case NSFetchedResultsChangeDelete: 
          [tableView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
         case NSFetchedResultsChangeUpdate: 
          [tableView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
        } 
       }]; 
      } 
     } 
     else if (self.objectChanges > 0) 
     { 
      NSMutableArray *indexPathsForUpdatedObjects = [NSMutableArray new]; 

      for (NSDictionary *change in self.objectChanges) 
      { 
       [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) { 

        NSFetchedResultsChangeType type = [key unsignedIntegerValue]; 
        switch (type) 
        { 
         case NSFetchedResultsChangeInsert: 
          [tableView insertRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
         case NSFetchedResultsChangeDelete: 
          [tableView deleteRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
         case NSFetchedResultsChangeUpdate: 
          [indexPathsForUpdatedObjects addObject:obj]; 
          //[tableView reloadRowsAtIndexPaths:@[obj] withRowAnimation:UITableViewRowAnimationAutomatic]; 
          break; 
         case NSFetchedResultsChangeMove: 
          [tableView moveRowAtIndexPath:obj[0] toIndexPath:obj[1]]; 
          break; 
        } 
       }]; 
      } 

      [tableView endUpdates]; 

      for (NSIndexPath *indexPath in indexPathsForUpdatedObjects) 
       if ([tableView.indexPathsForVisibleRows containsObject:indexPath]) 
        [self updateCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
     } 

     [self.sectionChanges removeAllObjects]; 
     [self.objectChanges removeAllObjects]; 

    } 
    else if (totalChanges < 1) 
    { 
     [tableView reloadData]; 
    } 
} 

- (NSMutableArray *)sectionChanges 
{ 
    if (!_sectionChanges) 
     _sectionChanges = [NSMutableArray new]; 

    return _sectionChanges; 
} 
- (NSMutableArray *)objectChanges 
{ 
    if (!_objectChanges) 
     _objectChanges = [NSMutableArray new]; 

    return _objectChanges; 
} 

注該解決方案並不完美,因爲如果對部分進行了更改並且這些對象位於未更新的部分中,則不會更新對象。應該很容易解決(並且對許多應用程序而言是無關緊要的)。