25

То, что я хочу сделать, довольно просто. В моем UITableViewController я хочу загрузить данные из нескольких NSFetchedResultControllers (у меня есть несколько объектов в моей модели данных) и поместить данные из каждого в другой раздел в виде таблицы. Так, например, все выбранные элементы из первого NSFetchedResultController будут находиться в разделе 0 в UITableView, извлеченные элементы из другого отправляются в раздел 1 и т. Д.Основные данные: UITableView с несколькими NSFetchedResultControllers

Проект шаблонов основных данных не показывает, как сделай это. Все (главным образом, указательные пути) кодируются без учета разделов (в шаблоне по умолчанию нет разделов), и все взято из одного NSFetchedResultController. Есть ли примеры проектов или документации, которые демонстрируют это?

Благодаря

ответ

24

Предположим на мгновение следующее в вашем заголовке (код ниже будет немного коряво, мои извинения):

NSFetchedResultsController *fetchedResultsController1; // first section data 
NSFetchedResultsController *fetchedResultsController2; // second section data 

Пусть таблица знаю, что вы хотите иметь 2 секции:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return 2; // you wanted 2 sections 
} 

дать ему названия разделов:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 
    return [NSArray arrayWithObjects:@"First section title", @"Second section title", nil]; 
} 

Пусть таблица знать, сколько строк есть в разделах:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    if (section == 0) { 
     return [[fetchedResultsController1 fetchedObjects] count]; 
    } else if (section == 1) { 
     return [[fetchedResultsController2 fetchedObjects] count]; 
    } 

    return 0; 
} 

Построить клетку:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    ... // table cell dequeue or creation, boilerplate stuff 

    // customize the cell 
    if (indexPath.section == 0) { 
     // get the managed object from fetchedResultsController1 
     // customize the cell based on the data 
    } else if (indexPath.section == 1) { 
     // get the managed object from fetchedResultsController2 
     // customize the cell based on the data 
    } 

    return cell; 
} 
+0

Спасибо за подробный ответ. Эта часть кажется довольно простой, однако есть еще 2 метода, которые я не так уверен (что они делают, и если им нужны какие-либо изменения): http://pastebin.ca/1805761 – indragie

+0

Это зависит от того, приложение делает; мне сложно ответить, не зная намного больше о приложении, дизайне и т. д. Однако у вас, вероятно, есть безопасный выбор, только когда эти методы отправляют табличное представление сообщения reloadData. – Giao

+0

Умело получить эту работу с небольшим мастерингом :-) Спасибо – indragie

3

Продление решения Giao с двумя NSFetchedResultsControllers - мы должны помнить наши NSFetchedResultsController не знают о наши два раздела и возвращенные NSIndexPathes будут всегда для первого раздела.

Так что, когда мы получаем объект в конфигурации ячейки:

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 
    if (!cell) { 
     [tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"]; 
     cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 
    } 
    [self configureCell:cell atIndexPath:indexPath]; 
    return cell; 
} 

-(void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath *)indexPath { 
    if (indexPath.section == 0) { 
     NSManagedObject *object = [self.fetchedResults1 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; 
     //use object to configure cell 
    } else { 
     NSManagedObject *object = [self.fetchedResults2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; 
     //use object to configure cell 
    } 
} 

Обновление клеток в то время как NSFetchedResultsController заметили некоторые изменения:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 
    NSIndexPath *customIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(controller == self.fetchedResultsController1)?0:1]; 
    NSIndexPath *customNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(controller == self.fetchedResultsController2)?0:1]; 
    switch(type) { 
     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 
     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 
+0

Хороший ответ, но после перезапуска приложения 'performFetch' нужно вызывать иначе, контекст теряет выбранные ManagedObjects. Любая помощь? – Goppinath

1

Multiple выборки контроллеров (и, возможно, несколько объектов) является неправильный подход , Правильное решение состоит в том, чтобы использовать параметр sectionNameKeyPath для NSFetchedResultController, чтобы сгруппировать результаты по нескольким разделам. Если вы думаете о своих сущностях по-разному, возможно, они на самом деле являются одним и тем же сущностью, и вместо этого вы можете использовать свойство itemType, которое затем можно разделить (и вы также должны его отсортировать). Например. скажем, у меня были сущности Hops и Grains, тогда я мог бы изменить их на Ingredient и иметь свойство int_16 property.Type, которое затем имеет перечисление в коде для хранения значений hopType = 0, grainType = 1. Ведь ингредиент - это просто имя и вес, которые оба из них разделяют.

Если, однако, ваши объекты действительно имеют определенный набор свойств, то правильным решением является создание родительского абстрактного объекта, обладающего свойством, которое вы можете использовать для разделения, например. sortOrder, sectionID или тип. Когда вы затем создаете контроллер выборки и извлекаете абстрактный родительский объект, вы фактически получаете результаты, содержащие все сущности. Например, в приложении Notes у них есть абстрактный объект NoteContainer, у которого есть сущность и учетная запись.Таким образом, они могут использовать один контроллер выборки для отображения учетной записи в первой ячейке раздела, а затем иметь все папки в следующих ячейках. Например. Все iCloud Notes (на самом деле это учетная запись), затем Notes (это папка по умолчанию), а затем все пользовательские папки, а затем папка с корзиной. Они используют свойство sortOrder, а папка по умолчанию - 1, для всех пользовательских папок - 2, а трещина - 3. Затем, добавив это как дескриптор сортировки, они могут отображать ячейки в том порядке, в котором они хотят. Это немного отличается от вашего требования, потому что у них есть два объекта, смешанных в разные разделы, но вы все равно можете использовать его только с разными свойствами сортировки.

Мораль истории не воюет с каркасом, обнимает ее :-)

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