После нескольких дней исследований и перекодирования я в значительной степени озадачен. Моя цель - запустить тестовое приложение с одним табличным представлением, заполненным двумя отдельными fetchedResultControllers.
У меня в списке покупок есть ряд предметов, каждый с отделом и логическим флагом «собрана». Неотобранные предметы должны быть перечислены по отделам, после чего следует отдельный раздел, содержащий все собранные предметы (независимо от отдела). Когда пользователь выбирает не собранные предметы, они должны перейти в раздел «собранные». Если он не проверяет собранный предмет, он должен вернуться в свой правильный отдел.
![enter image description here](https://i.stack.imgur.com/sgBVY.png)
Чтобы выполнить первую часть (не собранные элементы), я установил простой fetchedResultsController, который выбирает все элементы, где собрано = НЕТ, и распределяет результаты по отделам:
- (NSFetchedResultsController *)firstFRC {
// Set up the fetched results controller if needed.
if (firstFRC == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// fetch items
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// only if uncollected
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(collected = NO)"];
[fetchRequest setPredicate:predicate];
// sort by name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// fetch results, sectioned by department
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"department" cacheName:nil];
aFetchedResultsController.delegate = self;
self.firstFRC = aFetchedResultsController;
}
return firstFRC;
}
Я устанавливаю количество строк, разделов и заголовков разделов следующим образом:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return firstFRC.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[[firstFRC sections] objectAtIndex:section] name];
}
Я также использую шаблон контроллера controllerWillChangeContent, didChangeObject и controllerDidChangeContent для добавления / удаления ячеек в качестве изменения FRC.
Для краткости, я не буду включать код для отображения ячейки, но я по сути вытаскиваю правильный элемент на основе пути индекса ячейки, устанавливаю текст / субтитры ячейки и прикрепляю одно из двух изображений галочек, в зависимости от от того, собран ли предмет или нет. Я также подключаю это изображение (которое находится на кнопке), чтобы переключаться с отмеченного на непроверенное при касании, и соответствующим образом обновляю элемент.
Эта часть отлично работает. Я могу просмотреть свой список предметов по отделам, и когда я отмечаю один как собранный, я вижу его выпадающим из списка, как и ожидалось.
Теперь я попытался добавить нижний раздел, содержащий все собранные предметы (в одном разделе). Сначала я установил второй fetchedResultsConroller, на этот раз для выборки только несобранных элементов и без секционирования. Мне также пришлось обновить следующее:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections - add one for 'collected' section
return firstFRC.sections.count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section
if (section < firstFRC.sections.count) {
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
else {
return secondFRC.fetchedObjects.count;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section < firstFRC.sections.count) {
return [[[firstFRC sections] objectAtIndex:section] name];
}
else{
return @"Collected";
}
}
Затем я обновил cellForRowAtIndexPath аналогичным образом, так что элемент, который я извлекаю, происходит из правого FRC:
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
[[cell textLabel] setText:[item name]];
…rest of cell configuration
Это прекрасно работает, когда я запускаю. Вид таблицы отображается точно так, как ожидалось:
![enter image description here](https://i.stack.imgur.com/o0jlU.png)
Проблема (наконец)
(Первая) проблема возникает, когда я выбираю галочку для невыбранного элемента. Я ожидаю, что элемент будет удален из отдела, в котором он указан, и перемещен в раздел «собранные». Вместо этого я получаю:
CoreData: ошибка: серьезная ошибка приложения. Исключение было поймано
от делегата NSFetchedResultsController во время вызова
-controllerDidChangeContent :. Неверное обновление: недопустимое количество строк в разделе 0. Количество строк в существующем разделе
после обновления (2) должно быть равно числу строк, содержащихся в
этот раздел до обновления (2), плюс или минус количество строк
вставлен или удален из этого раздела (1 вставлен, 0 удален) и плюс
или минус количество строк, перемещенных в или из этого раздела (0 перемещено
в, 0 переехал). with userInfo (null)
Если я попытаюсь сделать обратное, я получу другую ошибку:
* Завершение работы приложения из-за необработанного исключения 'NSRangeException', причина: '* - [__ NSArrayM objectAtIndex:]: индекс 2 за пределами [0 ..
1]
Я подозреваю, что в обоих случаях существует проблема с согласованностью с количеством секций / строк в FRC и табличным представлением, когда элемент перемещается из одного FRC в другой. Хотя эта вторая ошибка заставляет меня думать, что, возможно, есть более простая проблема, связанная с моим поиском предметов.
Любое направление или идеи будут оценены. Я могу предоставить больше своего кода, если это поможет, а также создал небольшое тестовое приложение с одним представлением, чтобы продемонстрировать проблему. Я могу загрузить его при необходимости, но в основном я хотел проверить проблему в небольшой песочнице.
Обновление - запрашивается дополнительный код
В соответствии с запросом, вот что происходит при касании галочки:
- (void)checkButtonTapped:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
if (indexPath != nil)
{
[self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)cell.accessoryView;
if (![item collected]) {
[item setCollected:YES];
[button setBackgroundImage:[UIImage imageNamed:@"checked.png"] forState:UIControlStateNormal];
}
else if ([item collected]){
[item setCollected:NO];
[button setBackgroundImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
}
NSError *error = nil;
if (![item.managedObjectContext save:&error]) {
NSLog(@"Error saving collected items");
}
}