ios - UITableViewCell 在关闭模态呈现的 ViewController 后不反射(reflect)更改

标签 ios uitableview core-data nsfetchedresultscontroller segue

我有一个带有 TableView 的 UIViewController,用于显示“ list ”项目。该 ViewController 还有一个按钮,可以触发 segue 以模态方式呈现另一个 UIViewController,供用户添加新的 list 项。

所有 list 项都存储在 CoreData 中。

新的 CoreData 实体由 SecondViewController 创建并存储。用户可以在点击触发关闭 SecondViewController 的按钮之前添加任意数量的 list 项。在这里,由于这个 SecondViewController 是模态呈现的,我假设它的事件与 FirstUIViewController 根本没有任何关系。

在包含 UITableViewFirstViewController 处,有一个 NSFetchedResultsController 再次在 CoreData 上执行获取请求并提供所有数据到UITableView。 NSFetchedResultsController 的委托(delegate)也指向此 FirstUIViewController

问题是,在解雇 SecondViewController 后,UITableView 似乎注意到新的 list 实体的插入,并插入了新的 UITableCell UITableView 内。然而,奇怪的是,这个新插入的行没有显示正确的cell.textlabel。仅显示空白 UITableViewCell,标签上没有任何内容。

下面是我配置 UITableViewCell 的方法。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ChecklistCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    ChecklistItem *item = [self.fetch_controller objectAtIndexPath:indexPath];
    cell.textLabel.text = item.name;    
}

这是我的 NSFetchedResultsControllerDelegate

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type) {
        case NSFetchedResultsChangeInsert:
            NSLog(@"*** controllerDidChangeObject - NSFetchedResultsChangeInsert");
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

强制[self.tableView reloadData]似乎有帮助。我知道这一点是因为我添加了一个按钮来调用此方法,并且单元格的标签也相应地刷新和更新。

因此,我尝试了这个,因为我希望它会在 SecondViewController 解除后被调用。然而,该方法被调用,因为 Reload Data! 已记录,但不知何故,tableView 仍然无法正确显示新添加的单元格的标签。

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self performFetch];
    [self.tableView reloadData];
    NSLog(@"Reload Data!");
}

我想知道这里出了什么问题。不知何故,在 SecondViewController 关闭后,self.tableView 不会重新加载。有什么建议吗?

## 已编辑

好吧,更奇怪的是我在- (void)viewDidAppear:(BOOL)animated内尝试了[self.tableView reloadData]。新的tableViewCell仍然无法正确显示其标签;然而,等待 30 秒后,标签就神奇地弹出了!这是否意味着我的 iPhone/iPhone 模拟器需要时间思考很多?

最佳答案

说明

你的核心数据保存需要时间。如果您正在保存数据,然后立即关闭模态视图 Controller ,则 performFetch 函数上会出现竞争条件。这就是为什么您会看到它在 viewWillAppear 函数中不起作用,但在您按下按钮时却起作用。它有时会在 viewDidAppear 中起作用,但不能保证。这取决于您是否使用模拟器以及系统的繁忙程度。

具体建议

由于您是从核心数据中提取数据,因此我建议您使用 NSFetchedResultsController 并实现 NSFetchedResultsControllerDelegate。这些是专门为将 coreData 绑定(bind)到 UITableViewController 而设计的。它监听更改并立即根据这些更改添加、删除或更新行。

使用代码更新

要正确实现NSFetchedResultsControllerDelegate,您只需从以下网站复制代码即可:http://developer.apple.com/library/ios/#documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html

一些提示:

  1. 您需要对 cellForRowAtIndexPath 的实现方式稍作更改。创建一个名为 configureCell 的新方法。这是一种很常见的模式,因此您可以在 Apple 和其他地方的许多示例代码中找到它。
  2. 从上面网站的“典型用途”部分复制代码。您不需要实现“用户驱动的更新”。一定要设置

    self.fetchedResultsController.delegate = self;

  3. 如果您想检查此代码是否正常工作,可以将断点或 NSLog 放入 controllerWillChangeContentcontroller:didChangeObject 中。

  4. 第一次之后您不需要再次调用performFetch。此代码适本地监听更改和更新。
  5. 正如我一直建议大家的那样——我喜欢 MagicalRecord——所以请使用它。

配置单元

下面的 configureCell:atIndexPath 方法由 NSFetchedResultsController 委托(delegate)调用。

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    Product *product = [_fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = product.name;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Product Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

请参阅此处的委托(delegate)实现代码。了解更新对象时如何调用configureCell。

- (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];
}

关于ios - UITableViewCell 在关闭模态呈现的 ViewController 后不反射(reflect)更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14942300/

相关文章:

ios - 为核心数据创建模型 URL

ios - TextField 在编辑时附加到键盘 - 如何使 ScrollView 看起来与编辑前一样?

ios - 将 Byte 数组转换为 CGImage

ios - ClipsToBounds 在单元格中不起作用

iphone - 无法弄清楚为什么我的应用程序在使用 NSKeyedArchivers/NSKeyedUnarchivers 时崩溃

ios - Obj-C : Delegation pattern, 将自己设置为委托(delegate)

ios - if 语句中的错误 bool 结果

ios - 解析服务器 'released in production'/environment

ios - 在检索已更改的 CloudKit 记录时识别记录更改的类型

Swift,父类(super class)中定义的函数,当从子类对象调用时返回子类的类名