【问题标题】:Core Data: UITableView with multiple NSFetchedResultControllers核心数据:具有多个 NSFetchedResultControllers 的 UITableView
【发布时间】:2011-01-19 11:48:12
【问题描述】:

我想要做的很简单。在我的 UITableViewController 中,我想从多个 NSFetchedResultControllers 加载数据(我的数据模型中有多个实体)并将每个实体的数据放入表视图的不同部分。例如,从第一个 NSFetchedResultController 获取的所有项目将进入 UITableView 的第 0 部分,从另一个获取的项目进入第 1 节,等等。

Core Data 模板项目没有演示如何执行此操作。一切(主要是索引路径)都是在不考虑节的情况下编码的(默认模板中没有节),并且一切都取自单个 NSFetchedResultController。是否有任何示例项目或文档可以演示这样做?

谢谢

【问题讨论】:

    标签: iphone cocoa-touch uitableview core-data nsfetchedresultscontroller


    【解决方案1】:

    多个获取控制器(可能还有多个实体)是错误的方法。正确的解决方案是使用sectionNameKeyPath 参数到NSFetchedResultController 将结果分组到多个部分。如果您以不同的方式考虑您的实体,那么它们实际上可能是同一个实体,而您可以使用属性itemType,然后您可以对其进行分区(并且您也必须对其进行排序)。例如。假设我有实体啤酒花和谷物,然后我可以将它们更改为成分并拥有一个 int_16 属性ingredientType,然后我在代码中有一个枚举来存储值hopType = 0grainType = 1。毕竟成分只是一个名称和一个重量,它们都是共享的。

    但是,如果您的实体确实具有一组不同的属性,那么正确的解决方案是创建一个父抽象实体,该实体具有可用于划分的属性,例如sortOrdersectionIDtype。然后,当您创建一个获取控制器并获取抽象父实体时,您实际上会获得包含所有子实体的结果。例如,在 Notes 应用程序中,它们有一个抽象父实体 NoteContainer,它具有子实体 AccountFolder。这使单个提取控制器能够在该部分的第一个单元格中显示帐户,然后在以下单元格中显示所有文件夹。例如。所有iCloud Notes(实际上是帐户),然后是Notes(是默认文件夹),然后是所有自定义文件夹,然后是垃圾文件夹。他们使用sortOrder 属性,默认文件夹为1,自定义文件夹均为2,垃圾箱为3。然后通过将其添加为排序描述符,他们可以让单元格按他们想要的顺序显示。这与您的要求有点不同,因为它们将 2 个实体混合到不同的部分,但您仍然可以通过不同的排序属性来使用它。

    故事的寓意是不要与框架抗争,拥抱它:-)

    【讨论】:

      【解决方案2】:

      用两个 NSFetchedResultsController 扩展 Giao 的解决方案 - 我们必须记住,我们的 NSFetchedResultsController 不知道我们的两个部分,并且返回的 NSIndexPathes 将始终用于第一部分。

      所以当我们在单元格配置中获取一个对象时:

      - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
          if (!cell) {
              [tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
              cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
          }
          [self configureCell:cell atIndexPath:indexPath];
          return cell;
      }
      
      -(void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath *)indexPath {
          if (indexPath.section == 0) {
              NSManagedObject *object = [self.fetchedResults1 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
              //use object to configure cell
          } else {
              NSManagedObject *object = [self.fetchedResults2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
              //use object to configure cell
          }
      }
      

      在 NSFetchedResultsController 注意到一些变化时更新单元格:

      - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
          NSIndexPath *customIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(controller == self.fetchedResultsController1)?0:1];
          NSIndexPath *customNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(controller == self.fetchedResultsController2)?0:1];
          switch(type) {
              case NSFetchedResultsChangeInsert:
                  [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                  break;
              case NSFetchedResultsChangeDelete:
                  [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                  break;
              case NSFetchedResultsChangeUpdate:
                  [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
                  break;
              case NSFetchedResultsChangeMove:
                  [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                  [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                  break;
          }
      }
      

      【讨论】:

      • 不错的答案,但在重新启动应用程序后必须调用“performFetch”,否则上下文会丢失获取的 ManagedObjects。有什么帮助吗?
      【解决方案3】:

      暂时假设您的标题中包含以下内容(下面的代码会有些草率,我很抱歉):

      NSFetchedResultsController *fetchedResultsController1; // first section data
      NSFetchedResultsController *fetchedResultsController2; // second section data
      

      让表格知道您想要有 2 个部分:

      - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
          return 2; // you wanted 2 sections
      }
      

      给它章节标题:

      - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
          return [NSArray arrayWithObjects:@"First section title", @"Second section title", nil];
      }
      

      让表格知道每个部分有多少行:

      - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
          if (section == 0) {
              return [[fetchedResultsController1 fetchedObjects] count];
          } else if (section == 1) {
              return [[fetchedResultsController2 fetchedObjects] count];
          }
      
          return 0;
      }
      

      构建单元:

      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          ... // table cell dequeue or creation, boilerplate stuff
      
          // customize the cell
          if (indexPath.section == 0) {
              // get the managed object from fetchedResultsController1
              // customize the cell based on the data
          } else if (indexPath.section == 1) {
              // get the managed object from fetchedResultsController2
              // customize the cell based on the data
          }
      
          return cell;
      }
      

      【讨论】:

      • 感谢您的详细回答。这部分看起来相当简单,但是还有另外两种我不太确定的方法(他们做了什么以及是否需要任何更改):pastebin.ca/1805761
      • 这取决于您的应用程序的功能;如果不了解更多有关应用程序、设计等​​的信息,我很难回答。但是,您可能有一个安全的选择,只是让这些方法向 table view 发送 reloadData 消息。
      • 稍微修改一下就成功了 :-) 谢谢
      • 嗨@Giao,感谢您的回答。我缺少的一件事:我需要在NSFetchedResultsController(fetchRequest: , managedObjectContext: , sectionNameKeyPath: , cacheName: ) 的 sectionNameKeyPath: 中指定一些东西。 sectionNameKeyPath:应该是nil?它应该是sectionIndexTitlesForTableView中定义的数组的相同字符串吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多