2013-07-20 3 views
2

Итак, у меня есть этот настраиваемый массив для определения разделов для моего UITableView, но, как вы можете видеть, все элементы извлекаются через Core Data и сортируются отдельно.Пользовательские разделы Array для CoreData

Это мой пользовательский код:

-(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; 
}  

UPDATE: Код для генерации shortTerm, midTerm и longTerm даты в соответствии с [NSDate date] в sectionIdentifier Примечание: Я сделал много NSDate и NSString категорий для этого ,

-(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; 
} 

ответ

3

Группировка представления таблицы на секцию с извлеченным контроллером результатов (FRC) требует два вещей:

  • Вы должны установить sectionNameKeyPath: для FRC. Это может быть любой атрибут (постоянный или временный) объекта или даже метод произвольного экземпляра подкласса управляемого объекта.
  • Первый дескриптор сортировки запроса FRC должен сортировать объект в том же относительном порядке, что и свойство/метод sectionNameKeyPath:. Этот дескриптор сортировки может использовать только постоянный атрибут объекта.

Вы уже отсортировали объекты в соответствии с атрибутом scheduledDate. Чтобы сгруппировать объекты в разделы, вам просто нужно добавить метод «подходящего» экземпляра в подкласс объекта управления .

Предположим, что у вас есть переменные даты shortTermLimit и midTermLimit. Тогда можно определить следующий метод в классе категории вашего управляемого объекта подкласса:

@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"; 
    } 
} 

также взглянуть на "DateSectionTitles" образец проекта из библиотеки разработчиков Apple, который работает аналогично групповым объектам в разделы по годам и месяцам.

+0

Спасибо за ваш ответ, это очень помогло мне, но можете ли вы помочь мне с этой ошибкой: «CoreData: FATAL ERROR: постоянный кеш информации раздела не соответствует текущей конфигурации. Вы незаконно мутировали запрос на выборку NSFetchedResultsController, его предикат или дескриптор сортировки без отключения кэширования или использования + deleteCacheWithName: «Он был сгенерирован при создании новой записи. – Souljacker

+0

Я думаю, что это может быть связано с методом 'sectionIdentifier' ... – Souljacker

+0

Прежде чем сравнивать даты в соответствии с строкой даты следующим образом:' [[self shortTermDateStrings] containsObject: dateString] '. – Souljacker

0

Добавить переходный атрибут к вашему Task сущности, назвать это что-то вроде sectionName. Когда объект сохраняется или когда установлен scheduledDate, вычислите новый sectionName и сохраните его. Установите sectionName в качестве ключевого пути, который будет использоваться FRC.

+0

Итак, для пояснения: Создайте 'sectionName' во время создания сущности? Но как насчет того, редактирует ли пользователь объект? Затем нужно снова создать новое имя раздела? Не будет ли проще сортировать массив каждый раз при перезагрузке таблицы? – Souljacker

+1

Точно, его легче изменить только при редактировании (возможно, 1 или 2 элемента), чем каждый раз, когда таблица перезагружается. – Wain

+0

+1 для уточнения предмета. – Souljacker

Смежные вопросы