Строки не вставляются в UITable - PullRequest
1 голос
/ 31 марта 2012

У меня есть приложение с табличным представлением, которое расширяет / сворачивает разделы, следуя примеру в образце приложения Apple для анимации и жестов в виде таблицы .У меня возникают проблемы при добавлении элемента в закрытый раздел: после этого раздел больше не открывается, и я получаю исключение при попытке открыть, а затем закрыть его.

Я проследил эток некоторым странным действиям в методах открытия / закрытия:

-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)section {

if (![[sectionHeaderArray objectAtIndex:section] isOpen]) {

    [[sectionHeaderArray objectAtIndex:section] setIsOpen:YES]; 

    NSLog(@"self.tableView: %@", self.tableView);
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    NSInteger countOfRowsToInsert = [sectionInfo numberOfObjects];

    NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
    for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
        [indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
    }

    // Apply the updates.
    [self.tableView beginUpdates];
    NSLog(@"Count of rows to insert: %d", [indexPathsToInsert count]);
    NSLog(@"Rows before insert: %d", [self.tableView numberOfRowsInSection:section]);
    [self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationTop];
    NSLog(@"Rows after insert: %d", [self.tableView numberOfRowsInSection:section]);
    [self.tableView endUpdates];
}

}


-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionClosed:(NSInteger)section {

if ([[sectionHeaderArray objectAtIndex:section] isOpen]) {

    [[sectionHeaderArray objectAtIndex:section] setIsOpen:NO];

    NSInteger countOfRowsToDelete = [self.tableView numberOfRowsInSection:section];

    if (countOfRowsToDelete > 0) {
        NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
        for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
            [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]];
        }
        [self.tableView beginUpdates];
        NSLog(@"Count of rows to delete: %d", [indexPathsToDelete count]);
        NSLog(@"Rows before delete: %d", [self.tableView numberOfRowsInSection:section]);
        [self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
        NSLog(@"Rows after delete: %d", [self.tableView numberOfRowsInSection:section]);

    }

    [self.tableView endUpdates];
}
}

Сообщения журнала показывают, что при открытии (вставка строк) вставляется> 0 строк, и при этом количество строк для этого раздела остается равным 0:

2012-03-31 13:36:17.454 QuickList7[5523:fb03] Count of rows to insert: 3
2012-03-31 13:36:17.454 QuickList7[5523:fb03] Rows before insert: 0
2012-03-31 13:36:17.454 QuickList7[5523:fb03] Rows after insert: 0

Это устанавливает несовместимое состояние между таблицей и источником данных, а затем, когда я пытаюсь "свернуть" раздел, я получаю следующее исключение:

2012-03-31 13:48:35.783 QuickList7[5523:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid table view update.  The application has requested an update to the table view that is inconsistent with the state provided by the data source.'

Как я могу вставить 3 строки, и все же в конечном итоге с 0 строк?

Спасибо,

Саша

Ответы [ 2 ]

1 голос
/ 01 апреля 2012

Я нашел проблему!Это было на самом деле в обработчике изменений fetchedResultsController.Он реагировал на изменения в закрытых секциях, из-за которых таблица находилась в плохом состоянии и не синхронизировалась с источником данных.Поэтому я добавил проверку для каждого обновления, чтобы вставлять / удалять / обновлять строки только в том случае, если открывающий раздел открыт.

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tv = self.tView;
switch(type) {
    case NSFetchedResultsChangeInsert:
        if ([[sectionHeaderArray objectAtIndex:newIndexPath.section] isOpen]) {
            [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
        break;

    case NSFetchedResultsChangeDelete:
        if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) {
            [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
        break;

    case NSFetchedResultsChangeUpdate:
        if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) {
            [self configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        }
        break;

    case NSFetchedResultsChangeMove:
        if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) {
            [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
        if ([[sectionHeaderArray objectAtIndex:newIndexPath.section] isOpen]) {
            [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
        }
        break;
}

}
0 голосов
/ 31 марта 2012

В моем приложении я реализовал подобное поведение совершенно по-другому, потому что я часто сталкивался с проблемами такого типа.

У меня есть таблица с MenuNameCell s, MenuItemCell sи статическая ячейка внизу.Только одно меню раскрывается за раз, а нажатие MenuNameCell расширяет или сворачивает это меню.Поскольку я храню MenuNameCell в своем собственном разделе, а MenuItemCell - в другом, мне нужно только вставлять / удалять целые разделы при перезагрузке таблицы.

Вот источник данных моей таблицы:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // number of menus, plus 1 if a menu is open, plus 1 static cell
    return [self.restaurant.menus count]+(self.menu != nil ? 1 : 0)+1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // if this section is our selected menu, return number of items, otherwise return 1
    int numberOfRowsInSection = ([self indexPathIsInMenuItemSection:section] ? [[self.menu items] count] : 1);
    return numberOfRowsInSection;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == [tableView numberOfSections]-1) {
        // ... set up and return static cell
    }
    if ([self indexPathIsInMenuItemSection:indexPath.section]) {
        // ... set up and return menu item cell
    } else  {
        // ... set up and return menu name cell
    }
}

и делегат моей таблицы:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    // return if it's a static cell
    if (indexPath.section==[tableView numberOfSections]-1)
        return;
    // if it's a menu name cell, close open menu and maybe expand this menu
    if (![self indexPathIsInMenuItemSection:indexPath.section]) {
        BOOL reset = self.menu == m;
        if (reset) [self reloadTableView:self.tableView withMenu:nil animated:YES autoscroll:NO];
        else [self reloadTableView:self.tableView withMenu:m animated:YES autoscroll:YES];
    }
}

Там было упомянуто несколько помощников:

- (BOOL)indexPathIsInMenuItemSection:(NSInteger)section
{
    // returns YES if section refers to our MenuItemCells
    int indexOfMenu = [self.restaurant getIndexOfMenu:self.menu];
    return indexOfMenu != -1 && section == indexOfMenu+1;
}

- (void)reloadTableView:(UITableView *)tableView withMenu:(Menu *)menu animated:(BOOL)animated autoscroll:(BOOL)autoscroll
{
    int oldIndex = [self.restaurant getIndexOfMenu:self.menu];
    int newIndex = [self.restaurant getIndexOfMenu:menu];

    [tableView beginUpdates];

    if (oldIndex != -1) {
        // index of [section for items] is oldIndex+1
        [tableView deleteSections:[NSIndexSet indexSetWithIndex:oldIndex+1] withRowAnimation:UITableViewRowAnimationTop];
    }
    if (newIndex != -1) {
        // index for [section for items] is newIndex+1
        [tableView insertSections:[NSIndexSet indexSetWithIndex:newIndex+1] withRowAnimation:UITableViewRowAnimationTop];
        [self setMenu:menu];
    } else {
        // no new menu
        [self setMenu:nil];
    }

    [tableView endUpdates];
    if (autoscroll) [self autoscroll];
}

- (void)autoscroll
{
    if (self.menu != nil) {
        int section = [self.restaurant getIndexOfMenu:self.menu];
        if (section != -1) {
            NSUInteger indexes[] = {section,0};
            NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2];
            [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
        }
    }
}

Поскольку мои данные загружаются асинхронно в другом месте, у меня есть этоконтроллер настроен на получение NSNotification, но он должен работать так же хорошо, чтобы вызывать его на viewDidAppear:

[self reloadTableView:self.tableView withMenu:self.menu animated:YES autoscroll:YES];

Надеюсь, это поможет!Дайте мне знать, если я смогу уточнить что-либо из этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...