【问题标题】:Custom Sections Array for CoreDataCoreData 的自定义部分数组
【发布时间】:2013-07-19 19:56:57
【问题描述】:

所以我有这个自定义生成的数组来定义我的UITableView 的部分,但正如您所见,所有项目都是通过 Core Data 获取并单独排序的。

这是我的自定义代码:

-(NSArray *)tasks {
    NSManagedObjectContext *managedObjectContext = [self.fetchedResultsController managedObjectContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];

    // retrive the objects with a given value for a certain property
    [request setPredicate:nil];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"scheduledDate" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
    aFetchedResultsController.delegate = self;

    NSError *error = nil;
    NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];

    if ((result != nil) && ([result count]) && (error == nil)){
         return [NSMutableArray arrayWithArray:result];
    }
    return [NSMutableArray array];
}

-(NSMutableArray *)sectionsArray {
    // Fetch the scheduledDate check if it is in any of the term arrays and dump it in that section.
    NSArray *tasks = [self tasks]; 
    if (tasks) {
        NSArray *scheduledTasks = [tasks valueForKey:@"scheduledDate"];
        scheduledTasks = [[[NSSet setWithArray:scheduledTasks] allObjects] sortedArrayUsingSelector:@selector(compare:)];

        NSMutableArray *shortTermTasks = [NSMutableArray array];
        NSMutableArray *midTermTasks = [NSMutableArray array];
        NSMutableArray *longTermTasks = [NSMutableArray array];

        for (NSDate *scheduledDate in scheduledTasks) {
            NSString *dateString = [NSString dateStringFromFullDate:scheduledDate];

            NSPredicate *pred = [NSPredicate predicateWithFormat:@"scheduledDate == %@", scheduledDate];
            NSManagedObject *task = [[tasks filteredArrayUsingPredicate:pred] lastObject]; 

            if ([[self shortTermDateStrings] containsObject:dateString]) {
                [shortTermTasks addObject:task];
            }
            if ([[self midTermDateStrings] containsObject:dateString]) {
                [midTermTasks addObject:task];
            }
            if ([[self longTermDateStrings] containsObject:dateString]) {
                [longTermTasks addObject:task];
            }
        }
        sectionsArray = [NSMutableArray arrayWithObjects:shortTermTasks,midTermTasks,longTermTasks, nil];
        return sectionsArray;
    }
    return [NSMutableArray array];
}

我研究并发现(NSFetchedResultsController *)fetchedResultsController 定义了这些部分的组织方式,现在的问题是如何在此处生成我的自定义数组。

我的(NSFetchedResultsController *)fetchedResultsController 看起来像这样:

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" 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:@"scheduledDate" ascending:YES];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];

    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController 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 _fetchedResultsController;
}    

更新:根据[NSDate date] 中的sectionIdentifier 生成shortTermmidTermlongTerm 日期的代码 注意:我为此制作了很多 NSDateNSString 类别。

-(NSArray *)shortTermDates {
    NSArray *fullDateList = [NSDate daysFrom:[NSDate date] to:[NSDate dateWithDaysFromNow:2]];
    NSMutableArray *daysList = [NSMutableArray array];

    NSString *dateString = nil;
    for (NSDate *date in fullDateList) {
        dateString = [NSString dateStringFromFullDate:date];
        [daysList addObject:dateString];
    }
    return fullDateList;
}

-(NSArray *)shortTermDateStrings {
    NSMutableArray *daysList = [NSMutableArray array];

    NSString *dateString = nil;
    for (NSDate *date in [self shortTermDates]) {
        dateString = [NSString dateStringFromFullDate:date];
        [daysList addObject:dateString];
    }
    return daysList;
}

-(NSArray *)midTermDates {
    NSDate *startDate = [[self shortTermDates] lastObject];
    int addition = 5;
    int dayFromLastTermDate = [NSDate extractDayFromDate:[[self shortTermDates] lastObject]];
    NSDate *date = [NSDate dateWithYear:[NSDate extractYearFromDate:startDate] month:[NSDate extractMonthFromDate:startDate] day:dayFromLastTermDate+addition hour:[NSDate extractHourFromDate:startDate] minute:[NSDate extractMinuteFromDate:startDate] second:[NSDate extractSecondFromDate:startDate]];
    NSArray *fullDateList = [NSDate daysFrom:startDate to:date];
    return fullDateList;
}

-(NSArray *)midTermDateStrings {
    NSMutableArray *daysList = [NSMutableArray array];

    NSString *dateString = nil;
    for (NSDate *date in [self midTermDates]) {
        dateString = [NSString dateStringFromFullDate:date];
        [daysList addObject:dateString];
    }
    return daysList;
}

-(NSArray *)longTermDates {
    NSDate *startDate = [[self midTermDates] lastObject];
    int addition = 8;
    int dayFromLastTermDate = [NSDate extractDayFromDate:[[self midTermDates] lastObject]];
    NSDate *date = [NSDate dateWithYear:[NSDate extractYearFromDate:startDate] month:[NSDate extractMonthFromDate:startDate] day:dayFromLastTermDate+addition hour:[NSDate extractHourFromDate:startDate] minute:[NSDate extractMinuteFromDate:startDate] second:[NSDate extractSecondFromDate:startDate]];
    NSArray *fullDateList = [NSDate daysFrom:startDate to:date];
    return fullDateList;
}

-(NSArray *)longTermDateStrings {
    NSMutableArray *daysList = [NSMutableArray array];

    NSString *dateString = nil;
    for (NSDate *date in [self longTermDates]) {
        dateString = [NSString dateStringFromFullDate:date];
        [daysList addObject:dateString];
    }
    return daysList;
}

- (NSString *)sectionIdentifier;
{
    NSDate *date = self.scheduledDate;
    if ([date compare:[[self shortTermDates] lastObject]] == NSOrderedAscending) {
        return @"0";
        if ([date compare:[[self midTermDates] lastObject]] == NSOrderedAscending) {
            return @"1";
        } else {
            return @"2";
        }
    }
    return nil;
}

【问题讨论】:

    标签: iphone ios uitableview core-data nsarray


    【解决方案1】:

    使用获取结果控制器 (FRC) 将表视图分组到多个部分需要两件事:

    • 您必须为 FRC 设置 sectionNameKeyPath:。这可以是实体的任何属性(持久性或瞬态),甚至可以是实体的任意实例方法 托管对象子类。
    • FRC 请求的第一个排序描述符必须将对象排序为与sectionNameKeyPath: 属性/方法相同的相对排序。此排序描述符只能使用 实体的持久属性。

    您已经根据scheduledDate 属性对对象进行了排序。分组 对象分成多个部分,您只需向托管对象添加一个“合适的”实例方法 对象子类。

    让我们假设您有一些日期变量shortTermLimitmidTermLimit。 然后您可以在您的类类别中定义以下方法 托管对象子类:

    @implementation Task (CategoryMethods)
    
    - (NSString *)sectionIdentifier;
    {
        NSDate *date = self.scheduledDate;
        if ([date compare:shortTermLimit] == NSOrderedAscending) {
            return @"0";
        } else if ([date compare:midTermLimit] == NSOrderedAscending) {
            return @"1";
        } else {
            return @"2";
        }
    }
    
    @end
    

    然后你使用参数创建 FRC

    sectionNameKeyPath:@"sectionIdentifier"
    

    现在所有在shortTermLimit 之前安排的任务都被分组到“0”部分, 在midTermLimit 之前安排的任务被分组到“1”部分, 剩下的任务被分组到“2”部分。

    请注意,相对排序(“0”、“1”、“2”)与排序描述符兼容 使用scheduledDate

    但是你不希望“0”、“1”、“2”作为节标题,所以剩下的就是修改 titleForHeaderInSection 委托方法:

    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
        NSString *name = [sectionInfo name];
        if ([name isEqualToString:@"0"]) {
            return @"Short Term";
        } else if ([name isEqualToString:@"1"]) {
            return @"Mid Term";
        } else {
            return @"Long Term";
        }
    }
    

    还可以查看 Apple Developer Library 中的 "DateSectionTitles" 示例项目,它的工作原理类似于 按年和月将对象分组。

    【讨论】:

    • 感谢您的回答,它对我帮助很大,但是请您帮我解决这个错误:CoreData: FATAL ERROR: The persistent cache of section information does not match the current configuration. You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName: 它是在创建新条目时生成的。
    • 在我根据日期字符串比较日期之前:[[self shortTermDateStrings] containsObject:dateString].
    • @Ravin455:如果分组到部分发生变化(例如,因为 shortTermLimitmidTermLimit 已更改),那么您必须使用 deleteCacheWithName 删除部分缓存。或者,您可以使用cacheName:nil 创建不带缓存的 FRC。如果你没有性能问题,我建议不要使用缓存。
    • @Ravin455:我的想法是您不需要日期或日期字符串列表,而只需要短期/中期/长期任务之间的两个“边界日期”。如果我理解正确,shortTermLimit 是“今天加上 2 天”,midTermLimit 是“今天加上 5 天”,所以您只需计算这两个日期一次。
    • 请注意,这将导致与您的谓词匹配的所有对象出错(这对大型数据集具有内存和性能影响)。
    【解决方案2】:

    为您的Task 实体添加一个临时属性,将其命名为sectionName。当实体保存或设置scheduledDate时,计算新的sectionName并存储。将 sectionName 设置为 FRC 使用的密钥路径。

    【讨论】:

    • 所以为了澄清:在实体创建期间生成sectionName?但是如果用户编辑实体呢?那么我应该再次生成新的 sectionName 吗?每次重新加载表格视图时对数组进行排序会不会更容易?
    • 没错,只在编辑时更改(可能是 1 或 2 项)比每次重新加载表时更容易。
    猜你喜欢
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-30
    • 2017-06-25
    • 2013-12-24
    相关资源
    最近更新 更多