核心数据 - 在对象中的多对关系更改属性后更新 UI

Posted

技术标签:

【中文标题】核心数据 - 在对象中的多对关系更改属性后更新 UI【英文标题】:Core Data - update UI after object in to-many relationship change property 【发布时间】:2014-01-03 23:05:02 【问题描述】:

我有一个看起来像这样的核心数据模型:

Author
======
- name
- bio

- books (to-many relationship)


Book
====
- title
- release date

- author (to-one relationship)  

我向用户呈现作者的表格视图,每个表格单元格代表一个作者,并显示他的name 和他写的最新一本书的title

为了显示作者列表,我使用NSFetchedResultsController 和以下NSFetchRequest

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Author"];
request.predicate = nil;
request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];
request.fetchBatchSize = 20;  

我的问题是:

我让用户在另一个屏幕上更改书名,并且我希望如果用户更改那本最新书的标题,作者表视图将更新并反映最近的更改。

如何在作者表视图中更新最新的书名?

【问题讨论】:

【参考方案1】:

如果您已经在视图控制器中嵌入了 NSFetchedResultController 并实现了所有委托方法(请参阅前面发布的 Duncan 的回答),那么:

    您将您的 VC 订阅到上下文更改通知(viewDidLoadinit

    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(contextUpdated:)                          
                                                 name:NSManagedObjectContextObjectsDidChangeNotification
                                               object:nil];
    

    实现contextUpdated 方法

    -(void)contextUpdated:(NSNotification*)notification
    
    // if the context changed on other screens
        NSManagedObjectContext *changedContext = notification.userInfo[@"managedObjectContext"];
        if ([changedContext isEqual: self.fetchedResultsController.managedObjectContext])
            [_tableView reloadData];
        
    
    

    确保您从通知中取消订阅您的 VC

    -(void)dealloc
    
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    

【讨论】:

【参考方案2】:

在 tableViewController 中实现 fetchedResultsController 委托方法,更新应该会自动反映在 tableView 中。

https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html

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

【讨论】:

我已经实现了这些委托,问题是它不适用于这种情况,因为我的表格视图存在作者,并且更改在 book 对象上。如果我有一个书籍表格视图,那么 UI 将立即更新,但是当书籍对象更改时,这里的委托不会在作者视图控制器上被调用。 如果你要更新 latestBook 属性,它应该被调用。请参阅我关于添加此类属性的其他答案。 您可能需要为更新添加属性 latestBookName 以触发刷新。【参考方案3】:

我的解决方案:在实体Author中添加一个名为“triggerUpdate”的布尔字段,其他名称和类型也可以;如果更新作者写的书,就这样设置,

author.triggerUpdate = @(!author.triggerUpdate.boolValue); 

原因是,任何更新作者的字段都会触发NSFetchedResultsChangeUpdate类型的通知。

通过这种方式,我解决了这个问题。如果你有更好的答案,请告诉我。

【讨论】:

以上是关于核心数据 - 在对象中的多对关系更改属性后更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

如何在 IOS Swift 中获取核心数据中的多对多关系?

NSPredicate 在核心数据中的多对多关系

coreData为关系表添加其他属性的多对多关系

核心数据:与状态的多对多关系

为核心数据手动创建多对多关系中的中间表

更新 Prisma 中的多对多关系