核心数据、UITableView 和 UISegmentedControl

Posted

技术标签:

【中文标题】核心数据、UITableView 和 UISegmentedControl【英文标题】:Core Data, UITableView, and UISegmentedControl 【发布时间】:2010-08-04 03:41:58 【问题描述】:

我是使用 Core Data 的新手,并且正在使用 UITableView。我有一个带有 UISegmentedController 的工具栏,我想根据所选索引过滤 UITableView 中的项目。换句话说,假设我有一个 UITableView 来显示 Books(存储在 Core Data 中)和一个 UISegmentedController 用段来显示“English”、“Spanish”和“French”的书。

这里有什么方法可以让所有东西都连接起来?当点击其中一个段时,我在 UISegmentedControl 的目标中做什么来改变周围的东西?

对不起,如果这是一个愚蠢的问题!

【问题讨论】:

【参考方案1】:

我会为每个段使用一个单独的 NSFetchedResultsController。这将允许您利用每个段的内置缓存并提高性能。

除了 Apple 的文档(和我的书)之外,您还可以阅读我在 PragPub 杂志上的文章 Touching The Core。

【讨论】:

我不禁要说,作为已经为社区付出了这么多的人,我非常惊讶你花时间回答我关于 SO 的问题。一百万谢谢! Marcus,最干净的实现方式是什么?如果您有三个段,这将需要三个 FRC,并且在 tableview 代码中包含所有这些 switch() case: 语句并不是很漂亮。也许创建一个typedef enum,其标签对应于各个段,然后将 FRC 添加到类中适当索引处的 NSArray 中?然后你所要做的就是引用[self.frcArray objectAtIndex:[segControl selectedSegmentIndex]] 之类的东西,否则会有switch() case。还是有更清洁的方法? 将它们作为 iVar 并拥有 currentFRC iVar。当点击分段控件时,您切换currentFRC 指向的内容并告诉tableView 重新加载。其他所有内容都与currentFRC 对话,并检查您的委托方法,以便它们忽略任何不是来自currentFRC 的消息。【参考方案2】:

为每个过滤器使用三个不同的数组是个好主意。将它们缓存在某处,以便在用户选择过滤器时不会延迟。要从 CoreData 存储中查找您要查找的信息,请使用 NSPredicate。

【讨论】:

在 Core Data 中使用数组的代码比您需要编写的要多。最好使用NSFetchedResultsController,因为这是它的设计目的。 适当指出:我特别喜欢 NSFetchedResultsController 使用某种缓存。我的大部分数据都是动态的,所以我想我认为大多数事情都在 NSMutableArrays 中 即使是动态数据,带有内存存储和NSFetchedResultsController 的Core Data 在几乎所有情况下都会减少代码并提高性能。值得深思。【参考方案3】:

您可以使用NSFetchedResultsController,当您单击段时,只需设置不同的谓词并再次执行获取。

【讨论】:

阅读:developer.apple.com/iphone/library/documentation/CoreData/… 用于创建和使用 NSFetchedResultsController。 如果您只使用一个,那么您将失去其缓存的优势。最好为每个细分使用一个。【参考方案4】:

我已经使用上述 Marcus 的指南实现了这一点(我是新手,所以它可能不是最好的方法)。我有一个分段控制器,其中包含“打开”、“进行中”和“关闭”三个选项。

在 ViewController.h 中,为每个段选项创建一个 iVar,并为将存储当前控制器的主控制器创建一个 iVar。

@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;

@property (nonatomic, retain) NSFetchedResultsController *inprogressFetchedResultsController;
@property (nonatomic, retain) NSFetchedResultsController *openFetchedResultsController;
@property (nonatomic, retain) NSFetchedResultsController *closedFetchedResultsController;

在 ViewController.m 中你需要创建延迟加载这些控制器的方法,所以我一共有三个。除了谓词和缓存名之外,它们基本相同,我在下面只展示了一个。

- (NSFetchedResultsController *)closedFetchedResultsController

    if (_closedFetchedResultsController != nil) 
        return _closedFetchedResultsController;
    

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Ticket" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"priority.name" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"status = 'Closed'"];
    [fetchRequest setPredicate:predicate];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"priority.name" cacheName:@"ClosedTickets"];
    aFetchedResultsController.delegate = self;

    self.closedFetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.closedFetchedResultsController performFetch:&error]) 
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    

    return _closedFetchedResultsController;

为您的段创建一个 IBAction,以便在更改时更改获取的结果控制器并重新加载表。

- (IBAction)statusChanged:(id)sender 
    switch (self.segmentControl.selectedSegmentIndex) 
        case 0:
            self.fetchedResultsController = self.inprogressFetchedResultsController;
            break;
        case 1:
            self.fetchedResultsController = self.openFetchedResultsController;
            break;
        case 2:
            self.fetchedResultsController = self.closedFetchedResultsController;
            break;
        default:
            break;

    
    [self.tableView reloadData];

就是这样!

注意我还将这一行添加到我的 ViewDidLoad 方法中,以便它最初将正确的选项加载到 fetchedResultsController 中。

self.fetchedResultsController = self.inprogressFetchedResultsController;

【讨论】:

以上是关于核心数据、UITableView 和 UISegmentedControl的主要内容,如果未能解决你的问题,请参考以下文章

具有多个实体和 uitableview 的核心数据

基于核心数据属性的 UITableView 部分

UITableView 中核心数据对象的多个副本

使用 NSFetchedResultsController 和核心数据移动时 UITableView 单元格消失

需要帮助在 Swift 的 UITableView 中显示核心数据

iOS - 将单元格添加到带有核心数据和 NSFetchedResultsController 的空 UITableView 部分