Использование настраиваемых разделов с NSFetchedResultsController?

Я делаю секционированную таблицу с полученными результатами, но мне трудно разработать пользовательские секции.

Обычно можно было бы просто сортировать по атрибуту и ​​использовать sectionNameKeyPath: для создания разделов. Но мой атрибут сортировки вычисляется на лету, и я не могу заставить fetchedResultsController его правильно использовать ...

Обновление. Используя приведенный ниже совет jbrennan, я действительно близок к предполагаемой функциональности. Я добавил категорию в NSDate, которая возвращает число "дней назад"; вставив это сюда, я получу разделы, основанные на этих числах:

NSFetchedResultsController *aFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
    managedObjectContext:managedObjectContext
    sectionNameKeyPath:@"myDateAttribute.daysAgo"
    cacheName:@"Root"];

Вот где я застрял: мне не нужно их сортировать по «дням назад», мне нужно их отсортировать с помощью некоторых вычислений, основанных на других атрибутах в сущности. Поэтому я не могу просто вызвать этот настраиваемый метод Category, мне нужно вызвать метод с аргументами, например:

[myDateAttribute sortingRoutine:thisObject.value]

Или что-то вроде того. Я надеюсь, что в этом есть какой-то смысл. Большое спасибо, если вы можете помочь :)


person Community    schedule 05.09.2009    source источник


Ответы (3)


Вы можете попробовать следующее.

Добавьте временный атрибут в вашу основную модель данных в сущности Task. Затем реализуйте

- (void)awakeFromFetch

в классе Task NSManagedObject. См. Его документацию. Внутри метода вам разрешено установить значение переходного свойства, используя значения других свойств. Обратите внимание, что существуют некоторые ограничения на то, что вы можете делать, но это хорошо объяснено в документации (в частности, вы не можете изменять отношения или передавать аргументы; однако, если вы можете вычислить свое временное свойство, используя только значения других свойств / отношения должны быть в полном порядке).

Как только вы это сделаете, вы просто используете свойство transient в качестве атрибута, который вы передаете, чтобы вернуть разделы.

person Community    schedule 06.09.2009
comment
Ага! Это прекрасно сработало, спасибо. Теперь приложение вылетает при сохранении новых задач, но я немного поработаю над этим и опубликую отдельный вопрос, если потребуется. :) - person Triz; 07.09.2009
comment
Ваше приложение не должно вылетать при сохранении: временные свойства не сохраняются, поэтому это никак не может быть причиной. - person Massimo Cafaro; 07.09.2009
comment
На самом деле сбой происходит не во время процесса сохранения, а после, когда представление переключается обратно на секционированную таблицу. Я не делал этого до этих изменений и больше ничего не делал, так что я думаю, что это связано. Возможно, awakeFromFetch: не вызывается, когда представление снова появляется? - person Triz; 07.09.2009
comment
awakeFromFetch: вызывается один раз, непосредственно перед выборкой объектов. Когда вы переключаетесь обратно на таблицу, вызываются методы делегата NSFetchedResultsController, если вы их реализовали. - person Massimo Cafaro; 08.09.2009
comment
Так что мне нужно поместить то же самое, что я вложил в awakeFromFetch:, в один из этих методов? Любая идея, которая была бы наиболее подходящей? (В стороне: как вы узнали об этом? Например, как вы узнали, когда вызывается awakeFromFetch:? Кажется, я не могу найти какую-либо информацию такого рода в справочной документации ...) - person Triz; 08.09.2009
comment
Нет, вам не нужно копировать один и тот же код. Попробуйте закомментировать случай NSFetchedResultsChangeUpdate следующим методом: controller: didChangeObject: atIndexPath: forChangeType: newIndexPath: посмотрите, имеет ли это значение. В документации говорится, что awakeFromFetch автоматически вызывается платформой Core Data после получения получателя. Фактически, этот метод вызывается ДО того, как данные будут загружены в NSManagedObject, который будет возвращен вам запросом на выборку. - person Massimo Cafaro; 09.09.2009
comment
У меня вылетает 'NSInvalidArgumentException', reason: 'keypath myTransientCoreDataProperty not found in entity <NSSQLEntity MyModelName id=7>. Этот ответ на связанный вопрос, похоже, указывает на то, что временные свойства не могут использоваться в дескрипторах сортировки или предикатах хранилищ данных SQLite Core (это то, о чем я думал, но этот ответ дал мне надежду!). - person pkamb; 04.08.2016

Я сделал что-то подобное в приложении для iPhone (которое скоро будет). Мои разделы были разбиты по датам примерно так: вчера, сегодня, завтра, в будущем ...

В любом случае, уловка для меня заключалась в добавлении категории в NSDate, чтобы определить, к какому разделу принадлежит мой извлеченный объект.

У моего управляемого объекта было свойство с именем dueDate, которое было NSDate. При настройке контроллера полученных результатов я использовал @"dueDate.relativeDate" в качестве пути ключа раздела.

В категории -relativeDate был объявлен как возвращающий NSString, а также как свойство readonly (любого из которых может быть достаточно, я не пробовал без обоих, но это не повредит наличие как метода, так и объявления свойства). Тогда я просто реализовал метод, и он работал красиво.

person Community    schedule 05.09.2009
comment
Хорошо, я думаю, что я с тобой там. Но что, если мои NSDates фактически находятся в отдельном объекте? У меня Tasks в качестве основных получаемых сущностей, а Resets связаны (ко многим). Таким образом, у каждого Task есть куча Reset дат, причем самая последняя из них здесь самая важная. Я пробовал использовать @"reset.specialDateMethod в качестве ключа сортировки, но это не нравится ... - person Triz; 06.09.2009
comment
Это зависит от того, что делает ваш specialDateMethod. В этом методе вы, вероятно, могли бы фильтровать свой набор сбросов с помощью NSPredicate, чтобы найти важный, а затем использовать его date как вам угодно. Вы также можете захотеть кэшировать Reset, потому что я полагаю, что это обойдется дорого. Возможно, ваш Task может иметь свойство вроде mostRecentReset или что-то еще, что уместно. - person jbrennan; 06.09.2009
comment
Более конкретно: чего ждут sectionNameKeyPath:? Должна ли это быть строка, соответствующая атрибуту объекта? Думаю, здесь я запутался. - person Triz; 06.09.2009
comment
Обновление: я думаю, что я близок. Но: я могу поместить только метод, который не принимает аргументов в sectionNameKeyPath:. Таким образом, использование @"myAttribute.myMethod" работает нормально, но мне нужно передать информацию о методе рассматриваемого объекта - мне нужно иметь возможность делать что-то вроде @"[myAttribute myMethod:var otherArg:otherVar]" Но это задыхается. - person Triz; 06.09.2009
comment
Вау, это отличное решение. Мне нужно было временное свойство в NSDate так же, как и вам, но я не мог полностью понять временные свойства. Размещение категории в NSDate помогло и стало чище, благодаря чему моя модель оставалась небольшой. Спасибо. - person Jaanus; 06.02.2010
comment
Очень умное и простое решение. Интересно, влияет ли это на производительность - person elitalon; 29.01.2013

Вот код для этого (спасибо jbrennan):

@implementation NSDate (MyExtensions)


// Return today.
+ (NSDate *)today {

    return [NSDate date];
}


// Return yesterday (today minus 24 hours).
+ (NSDate *)yesterday {

    return [NSDate dateWithTimeIntervalSinceNow:-60*60*24];
}


// Return tomorrow (today plus 24 hours).
+ (NSDate *)tomorrow {

    return [NSDate dateWithTimeIntervalSinceNow:60*60*24];
}


// Convert a date comporting a time into a rounded date (just the day, no time).
- (NSDate *)dayDate {

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents *comps = [[NSCalendar currentCalendar] components:unitFlags fromDate:self];
    return [[NSCalendar currentCalendar] dateFromComponents:comps]; 
}


// Return a string representing the date relatively (today, tomorrow, yesterday, etc.)
// If no relative sentence is found, return the NSDateFormatterLongStyle formatted date.
- (NSString *)relativeDate {

    if ([self.dayDate isEqualToDate:[[NSDate today] dayDate]]) {

        return NSLocalizedString(@"Today", @"NSDate extensions");
    }
    if ([self.dayDate isEqualToDate:[[NSDate yesterday] dayDate]]) {

        return NSLocalizedString(@"Yesterday", @"NSDate extensions");
    }
    if ([self.dayDate isEqualToDate:[[NSDate tomorrow] dayDate]]) {

        return NSLocalizedString(@"Tomorrow", @"NSDate extensions");
    }

    return [NSDateFormatter localizedStringFromDate:self
                                          dateStyle:NSDateFormatterLongStyle
                                          timeStyle:NSDateFormatterNoStyle];    
}


@end
person Community    schedule 16.01.2011
comment
Это устарело, но любой, кто это читает: НЕ используйте такие константы секунд, используйте функции NSCalendar. Посетите любой разговор WWDC на свиданиях - person Dmitry Shevchenko; 01.10.2012