使用显示顺序排序的NSFetchedResultsController部分。

10

我目前有一个选项,允许用户在我的 iPhone 应用中更改类别的显示顺序。

我想使用 NSFetchedResultsController 对表视图进行分组,使得分组的标题为“category.name”,并按照“category.displayOrder”的顺序排序,“category”与我正在获取的实体具有 TO-ONE 关系。我唯一能够使分组正确工作的方法是将“category.displayOrder”用作分组标题。

NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"category.displayOrder" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];

NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

[fetchRequest setFetchBatchSize:10];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                                    managedObjectContext:managedObjectContext
                                                                                                      sectionNameKeyPath:@"category.name"
                                                                                                               cacheName:nil];
有没有关于如何将章节标题命名为与我所排序的属性不同的想法?
4个回答

15

不确定我是否完全理解您的问题,但我在我的应用程序中执行类似操作,这是我如何让它起作用的:

首先,在获取控制器方法中,我根据自己的需求设置排序描述和谓词。在这种情况下,我希望按发布日期,然后按名称对电影标题进行排序。然后,使用谓词来获取特定“类型”的实体,并在特定的“发布日期”范围内。

在我的获取结果控制器定义中,将sectionNameKeyPath设置为“releaseDate”,因此我的部分标题将基于日期。

- (NSFetchedResultsController *)fetchedResultsController {

    if (fetchedResultsController_ != nil) {
        return fetchedResultsController_;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSSortDescriptor *sortByReleaseDate = [[NSSortDescriptor alloc] initWithKey:@"releaseDate" ascending:NO];
    NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortByReleaseDate,sortByName, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];
    [sortDescriptors release];
    [sortByName release];
    [sortByReleaseDate release];            
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(type == 'Movies') AND (releaseDate <= %@) AND (releaseDate >= %@)", [NSDate date], [NSDate dateWithTimeIntervalSinceNow:kOneDayTimeInterval*-30]];
    [fetchRequest setPredicate:predicate];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Movie" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"releaseDate" cacheName:nil];

    ...// Perform and return fetch here, error handling etc...

    return fetchedResultsController_;

}    

然后在我的表格视图数据源委托方法中,将我的NSDate转换为NSString,之后返回每个标题的实际字符串(记得必须返回NSString作为表格视图标题)。

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

    NSString *rawDateStr = [[[self.fetchedResultsController sections] objectAtIndex:section] name];
    //convert default date string to NSDate...
    NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZ"];
    NSDate *date = [formatter dateFromString:rawDateStr];

    //convert NSDate to format we want...
    [formatter setDateFormat:@"d MMMM yyyy"];
    NSString *formattedDateStr = [formatter stringFromDate:date];
    return formattedDateStr;

}

如果我想改变我的数据显示方式,例如按titleName组织,那么我需要将我的fetchedResultsController对象更改为:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"titleName" cacheName:nil];

修改我的tableview:titleForHeaderInSection:数据源方法,只需返回titleName(已经是字符串):

 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

        return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}

希望这能帮助你找到解决特定问题的方法。

祝好, Rog


1
我正在尝试按displayOrder(NSInteger)对我的部分进行排序,但是我不想显示整数作为部分标题,而是想显示与该displayOrder相对应的名称。因此,我将不再使用1、2和3作为部分标题,而是使用“FirstName”、“Second Name”和“Third Name”。您认为最好的方法是根据titleForHeaderInSection中的显示顺序确定标题吗? - avenged
据我所知,这是唯一的方法。您可以在displayOrder上进行switch,然后返回您想要显示的相应部分。 - Rog
我本希望不必在titleForHeaderInSection函数中再执行一次获取操作,但看起来似乎没有其他方法了... - avenged

11

我曾经遇到过相同的问题,并且我有另一种解决方法 - 使用一个暂存属性来作为sectionNameKeyPath,例如category.sectionName。似乎如果您使用core data属性作为sectionNameKeyPath,它将尝试使用该属性进行排序。

因此,以您上面提供的示例为例:

NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"category.displayOrder" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"category.sectionName" cacheName:nil];

Category.h:

@interface Category : NSManagedObject

@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* displayOrder;

-(NSString*) sectionName;

@end

Category.m:

@implementation Category

@dynamic name;
@dynamic displayOrder;

-(NSString*) sectionName{
    return self.name;
}

@end

然后在您的视图控制器中:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    id<NSFetchedResultsSectionInfo> sectionInfo=[self.resultsController.sections objectAtIndex:section];
    return sectionInfo.name;
}

3
感谢Rog的帮助,我已经理解了这个问题并找到了解决方案。虽然它不如我所愿那样清晰,但如果有更好的方法,我很乐意听取建议。
根据我所学(可能有误,请告诉我),您只能通过您正在对表格进行排序的属性来命名部分(sectionNameKeyPath)。在我的情况下,我想使用一个名为displayOrder的属性来对表格进行排序,但我不想让我的部分标题变成0、1、2等等。相反,我希望部分标题使用与displayOrder相对应的title属性“Name1”、“Name2”等。为了做到这一点,在titleForHeaderInSection中,我根据displayOrder确定了标题。
        NSString *displayOrder = [sectionInfo name];

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:[NSEntityDescription entityForName:@"Platform" inManagedObjectContext:managedObjectContext]];

        NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
        [numberFormatter setNumberStyle:NSNumberFormatterNoStyle];

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"displayOrder == %@", [numberFormatter numberFromString:displayOrder]];
        [fetchRequest setPredicate:predicate];

        Platform *platform;
        NSError *error = nil;
        NSArray *fetchResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
        if ([fetchResults count] > 0) {
            platform = [fetchResults objectAtIndex:0];
            headerView.titleLabel.text = [platform name];
        } else {
            headerView.titleLabel.text = @"Unknown";
        }

        [numberFormatter release];
        [fetchRequest release];

感谢Rog!

@avenged,你是怎么发现这个的?“根据我所学(我可能错了,请告诉我),你只能通过表格排序的属性来命名部分(sectionNameKeyPath)。” 这让我很困扰!谢谢! - Mr Rogers

0

在我的情况下,我必须阅读文档:

- initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:

如果sectionNameKeyPath与fetchRequest中的第一个排序描述符不同,则它们必须生成相同的相对顺序。例如,fetchRequest中的第一个排序描述符可能指定持久属性的键;sectionNameKeyPath可能指定从持久属性派生的瞬态属性的键。

sectionNameKeyPathNSSortDescriptor属性的排序顺序与另一个属性不同时,这会导致表格视图排序上的不一致。

Source:
https://richardwarrender.com/2010/10/core-data-objects-in-wrong-sections/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接