Why Can't I delete the bottom row of my UITableView?

时光毁灭记忆、已成空白 提交于 2019-12-05 16:05:33

Your design it at fault. You shouldn't be adding rows anywhere but where they logically go in the table.

The entire point of a fetched results controller (FRC) is to synchronize the tableview with the data. The order of the rows in the table should reflect the order of managed objects in the fetchedObjects array. By inserting a row at the bottom or top and while adding an object that does not necessarily logically belong at the top or bottom of the table, are breaking that synchronization.

When you add a new managed object in addingViewController:didAdd: the FRC alerts it delegate which tries to redraw the table. You've tried to compensate for this but you really can't. All you indexes are coming off.

Instead of using a row to input new rows. Use a tableview header or footer view. That way, you can freeze the tableview, create the new object, then update the table and the new object will show up where it logical belongs in the table.

You can add static cells to UITableViews that get their data from a NSFetchedResultsController. But to do this you have to adjust almost all NSIndexPaths that are used within one of the UITableViewDelegate, UITableViewDataSource or NSFetchedResultsControllerDelegate methods.

I added some helper methods that translate the indexpath of the tableview to the indexpath of the fetched resultscontroller and the other way around. Something like this could be used if you want to add a row on top:

- (NSIndexPath *)tableIndexPathFromNSFRCIndexPath:(NSIndexPath *)ip {
    if (editingMode && ip.section == 0) {
        NSIndexPath *newIP = [NSIndexPath indexPathForRow:ip.row+1 inSection:ip.section];
        return newIP;
    }
    return ip;
}

- (NSIndexPath *)nsfrcIndexPathFromTableIndexPath:(NSIndexPath *)ip {
    if (editingMode && ip.section == 0) {
        NSIndexPath *newIP = [NSIndexPath indexPathForRow:ip.row-1 inSection:ip.section];
        return newIP;
    }
    return ip;
}

and then you have to change every method that passes an indexpath from the table to the fetchedresultscontroller or from the frc to the table. I show you two as an example.

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath {
    newIndexPath = [self tableIndexPathFromNSFRCIndexPath:newIndexPath];
    indexPath = [self tableIndexPathFromNSFRCIndexPath:indexPath];
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.listTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [self.listTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.listTableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [self.listTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.listTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [aTableView deselectRowAtIndexPath:indexPath animated:YES];
    if (editingMode && indexPath.section == 0 && indexPath.row == 0) {
        // Add New entry...
    }
    else {
        indexPath = [self nsfrcIndexPathFromTableIndexPath:indexPath];
        NSManagedObject *selectedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]);
    }
}

To not get the rows mixed up I would suggest putting the insert row in its own section. Since you're obviously just using one section you know that the section you send to FRC should always be 0. The code would be as simple as:

[context deleteObject:[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]];

TechZen's solution will also work, so which solution you choose is entirely about what design you prefer. TechZen's solution doesn't interfere with having multiple sections, but this solution could be modified to support multiple sections as well.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!