Ios 未插入UITable中的行

Ios 未插入UITable中的行,ios,uitableview,Ios,Uitableview,我有一个带有表视图的应用程序,它可以展开/折叠部分,就像苹果的应用程序一样。当一个项目被添加到一个已关闭的部分时,我遇到了问题:在这之后,该部分不再打开,当我尝试打开然后关闭它时,我会遇到一个异常 我将其追溯到打开/关闭方法中的一些奇怪行为: -(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)section { if (![[sectionHeaderArray obj

我有一个带有表视图的应用程序,它可以展开/折叠部分,就像苹果的应用程序一样。当一个项目被添加到一个已关闭的部分时,我遇到了问题:在这之后,该部分不再打开,当我尝试打开然后关闭它时,我会遇到一个异常

我将其追溯到打开/关闭方法中的一些奇怪行为:

-(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];
}
}
这会在表和数据源之间设置不一致的状态,然后当我尝试“折叠”节时,会出现以下异常:

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行结束

谢谢


Sasha

在我的应用程序中,我以一种非常不同的方式实现了类似的行为,因为我经常遇到这种类型的问题

我有一个带有
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];

我希望这有帮助!请告诉我是否可以澄清任何问题。

在我的应用程序中,我以非常不同的方式实现了类似的行为,因为我经常遇到此类问题

我有一个带有
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];

我希望这有帮助!如果我能澄清任何问题,请告诉我。

我发现了问题!它实际上位于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;
}

}

我发现了问题!它实际上位于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;
}

}

谢谢你,斯宾塞!在找到苹果的示例代码之前,我大概就是这么想的。不过,我没有想到将标题单元格和内容单元格放在单独的部分,这是个好主意。我想在改变我的方法之前,我会坚持下去,看看是否有人有其他建议(因为我很懒:)。但如果我必须重写的话,我肯定会用它作为模板。谢谢谢谢你,斯宾塞!在找到苹果的示例代码之前,我大概就是这么想的。不过,我没有想到将标题单元格和内容单元格放在单独的部分,这是个好主意。我想在改变我的方法之前,我会坚持下去,看看是否有人有其他建议(因为我很懒:)。但如果我必须重写的话,我肯定会用它作为模板。谢谢这是个棘手的问题!我也有同样的问题,我花了好几个小时才发现这与没有扩展的部分有关。这是一个棘手的问题!我也有同样的问题,我花了好几个小时才发现这与没有扩展的部分有关。