不同 tableView 部分中的核心数据对象

Posted

技术标签:

【中文标题】不同 tableView 部分中的核心数据对象【英文标题】:Core data objects in different tableView sections 【发布时间】:2014-01-10 16:01:39 【问题描述】:

我有一个带有可展开/可折叠部分和固定部分标题的 tableView,我想对其进行维护。 这是执行此操作的代码:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return 6;



+ (NSString*) titleForHeaderForSection:(int) section

    switch (section)
    
        case 0 : return @"Overdue";
        case 1 : return @"Today";
        case 2 : return @"Tomorrow";
        case 3 : return @"Upcoming";
        case 4 : return @"Someday";
        case 5 : return @"Completed";
       // default : return [NSString stringWithFormat:@"Section no. %i",section + 1];
    

目前,我正在使用以下代码填充行:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    switch (section)
    
        case 2 : return 1;
        case 3 : return 30;
        default : return 8;
    


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

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
    
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    

    // Configure the cell.

    switch (indexPath.row)
    
        case 0 : cell.textLabel.text = @"First Cell"; break;
        case 1 : cell.textLabel.text = @"Second Cell"; break;
        case 2 : cell.textLabel.text = @"Third Cell"; break;
        case 3 : cell.textLabel.text = @"Fourth Cell"; break;
        case 4 : cell.textLabel.text = @"Fifth Cell"; break;
        case 5 : cell.textLabel.text = @"Sixth Cell"; break;
        case 6 : cell.textLabel.text = @"Seventh Cell"; break;
        case 7 : cell.textLabel.text = @"Eighth Cell"; break;
        default : cell.textLabel.text = [NSString stringWithFormat:@"Cell %i",indexPath.row + 1];
    

    //cell.detailTextLabel.text = ...;

    return cell;

但我想从核心数据实体填充行,这是我的 NSFetchedResultsController:

- (NSFetchedResultsController *)fetchedResultsController 

    if (_fetchedResultsController != nil) 
        return _fetchedResultsController;
    

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription
                                   entityForName:@"ToDoItem" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                              initWithKey:@"tdText" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
                                                   cacheName:@"Root"];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;


核心数据实体是'ToDoItem',我最终想要的是每个对象都出现在tableView的右侧部分中。我需要你的帮助和建议。


为了让我的问题更清楚一点,我将创建一个名为 tdDate 的属性,该属性应该是过滤键,以决定在哪个部分显示对象。考虑到具有当前日期的对象今天将出现在 TODAY 部分下,明天它应该出现在 OVERDUE 部分下......

【问题讨论】:

【参考方案1】:

实现的一个好方法是在您的ToDoItem 中添加一个新属性(例如state),您可以将其用作sectionNameKeyPathNSFetchedResultsController

我会尝试用一个简单的例子来解释。

想想列出食物的Food 实体。这个实体有一个name 和一个category。例如

Apple, Fruit
Banana, Fruit
Potato, Vegetable
Salad, Vegetable

如果您使用category 作为sectionNameKeyPath,您会发现两个部分。一个将包含属于Fruit 类别的食物,另一个将包含Vegetable 一个。

如果在您的实体中使用 state 属性,同样适用(为简单起见,它是一个字符串,但您可以只使用数字并使用映射将每个数字映射到一个字符串值)。我想这是正确的,因为每个项目都会有一个完成状态。 此外,通过这种方法,与每个 state 关联的值可以动态变化。这应该是模型的一部分,而不是控制器的一部分。 因此,state 可以假定以下可能值之一:@"Overdue"@"Today"@"Tomorrow"@"Upcoming"@"Someday"@"Completed"

作为重要说明(请参阅 Apple 文档)

如果控制器生成节,则第一个排序描述符在 数组用于将对象分组;它的钥匙必须要么 与 sectionNameKeyPath 或使用其的相对排序相同 键必须使用 sectionNameKeyPath 匹配。

【讨论】:

谢谢,但在我的情况下,将对象分配到部分的关键可能每天都在变化。 @mvasco 你什么意思? 我的意思是这些部分应该包含有日期的 ToDoItems(可能是一个名为 tdDate 的属性),这个属性可以用作过滤键来使分布成部分,但是我应该找到根据当前日期更新密钥的方法。 考虑到您的回答,我创建了一个名为 tdDate 的新属性,其中包括 ToDoItem 的到期日期。现在我有两个问题:1.如何在 tableView 上显示对象,2.如何将它们过滤到部分(过期,今天,明天,...)我是真正的解决方案。 @mvasco 您能否打开一个新答案并将链接作为评论发布?如果可能的话我会帮忙的。谢谢【参考方案2】:

我能想到至少两种合理的策略。一种是进行多次单独的提取,每个类别一次。每个获取请求都将被赋予一个谓词,该谓词选择那些到期日期和状态适合每个类别的 ToDoItem 对象。例如,“今天”的获取请求将选择到期日期为当前日期且状态不完整的项目; “已完成”的请求将忽略日期并选择所有状态为已完成的项目。

另一种策略是一次获取所有项目并在获取后自行对它们进行分类。这在概念上可能更简单,您仍然可以使用 NSPredicate 对对象进行分类。比如可以使用NSSet的-objectsPassingTest:方法或者NSArray的-filteredArrayUsingPredicate:方法。

花一些时间阅读using predicates和fetching objects。

【讨论】:

您介意给我举个例子,说明如何用获取请求的结果替换当前使用 switch 循环的 cellForRowAtIndexPath 吗? 假设您有一个数组,其中包含所有明天到期的项目。使用indexPath.row 作为数组的索引,例如:ToDoItem* item = tomorrowItems[indexPath.row]; cell.textLabel.text = item.title; 您可以将相同的策略应用于各个部分——也许您有一个数组,其中数组中的每个项目都在,并且特定部分的项目数组。然后您可以使用indexPath.section 获取正确的数组,然后使用indexPath.row 从该数组中获取项目:ToDoItem* item = (allItems[indexPath.section])[indexPath.row]; 我创建了一个名为 tdDate 的新属性,其中包括 ToDoItem 的到期日期。你能告诉我在 tableView 上显示对象并过滤它们以便包含在正确的部分中所需的代码吗? 您可以通过填充-tableView:cellForRowAtIndexPath: 中的单元格来显示桌子上的对象,就像您正在做的那样。对不起,我没有时间为你写代码。如果您阅读我上面链接的文档,如何对项目进行分类应该很清楚。尝试一下,如果遇到问题,请在此处寻求帮助。【参考方案3】:

您应该能够通过计算 ToDoItem 应出现在的正确部分(即 0=过期、1=今天、2=明天)的瞬态属性(例如 itemState)对获取的结果进行分组。

[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"itemState"
                                                   cacheName:@"Root"];

在您的托管对象中,实现 itemState 以返回项目的计算状态:

- (NSString *)itemState 
    // logic to return @"Overdue", @"Today", etc.. based on whatever logic makes sense in your app
    // see this on how to implement transient properties http://davemeehan.com/technology/objective-c/core-data-transient-properties-on-nsmanagedobject

然后你可以继续使用你的 FRC 来实现你的 UITableViewControllerDataSource 方法:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return [self.fetchedResultsController sections].count;

...等等...

【讨论】:

我猜这不起作用,因为itemState会在请求检索到的对象中找到。 @flexaddicted 不确定你的意思? @flexaddicted 哦,我想我明白你的意思了。因为他想要一组固定的部分,所以这不会按原样工作(例如,可能没有过期的项目,所以过期不会出现)。

以上是关于不同 tableView 部分中的核心数据对象的主要内容,如果未能解决你的问题,请参考以下文章

将tableview中的所有值保存到iOS中的数组?

使用核心数据和数组从 TableView 中插入和删除行

Tableview 中的 Xcode 核心数据

使用具有不同单元格数的部分的 UITableView 的核心数据

无法在设备之间进行 iCloud 同步

在手动创建的部分中对核心数据 tableView 行进行分组