不同 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
),您可以将其用作sectionNameKeyPath
的NSFetchedResultsController
。
我会尝试用一个简单的例子来解释。
想想列出食物的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 部分中的核心数据对象的主要内容,如果未能解决你的问题,请参考以下文章