尝试删除表中的行'Invalid update: invalid number of rows in section 0'出错



我的代码似乎运行得很好,但当我在UITableView中滑动删除一行时,应用程序崩溃了:

错误

LittleToDoApp[70390:4116002] ***由于未捕获异常'NSInternalInconsistencyException'而终止应用程序,原因:'无效更新:无效的行数在section 0中。

更新(1)后的现有部分所包含的行数必须等于更新(1)之前该部分所包含的行数,加上或减去从该部分插入或删除的行数(0插入,1删除),加上或减去该部分移进或移出的行数(0移进,0移出)。

ViewController.m

#import "ViewController.h"
#import "ToDoItem.h"
#import "ToDoItemSvcCache.h"
@interface ViewController ()
@end
@implementation ViewController
@synthesize tableView;
ToDoItemSvcCache *ToDoItemSvc = nil;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    ToDoItemSvc = [[ToDoItemSvcCache alloc] init];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)deleteToDoItem:(id)sender {
    NSLog(@"Deleting ToDoItem");
    [self.view endEditing:YES];
}
- (IBAction)addToDoItem:(id)sender {
    [self.view endEditing:YES];
    NSLog(@"saveToDoItem: entering");
    ToDoItem *todoitem = [[ToDoItem alloc] init];
    todoitem.todoitem = _toDoItem.text;
    [ToDoItemSvc createToDoItem:todoitem];
    [self.tableView reloadData];
    NSLog(@"saveToDoItem: todoitem saved");
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *simpleTableIdentifier = @"toDoItemCell";
    UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                  reuseIdentifier:simpleTableIdentifier];
    }
    ToDoItem *toDoItem = [[ToDoItemSvc retrieveAllToDoItems]
                    objectAtIndex:indexPath.row];
    cell.textLabel.text = [toDoItem description];
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    return [[ToDoItemSvc retrieveAllToDoItems] count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"viewToDoItem"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        SecondViewController *destViewController = segue.destinationViewController;
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        destViewController.toDoItemName = cell.textLabel.text;
    }
}
#pragma hiding status bar
- (BOOL)prefersStatusBarHidden {
    return YES;
}
// here we get back from both styles
- (IBAction)unwindFromDetailViewController:(UIStoryboardSegue *)segue
{
    // UIViewController *detailViewController = [segue sourceViewController];
    NSLog(@"%@", segue.identifier);
}
//Allows the delete button to show up when left swipping a list item
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return YES - we will be able to delete all rows
    return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Will add code to actually delete a row here. Adding NSLog so we know its triggering though
    NSLog(@"Deleted row.");
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView reloadData];
}
@end

ToDoItemSvc.h

#import <Foundation/Foundation.h>
#import "ToDoItem.h"
@protocol ToDoItemSvc <NSObject>
    - (ToDoItem *) createToDoItem: (ToDoItem *) todoitem;
    - (NSMutableArray *) retrieveAllToDoItems;
    - (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem;
    - (ToDoItem *) deleteToDoItem: (ToDoItem *) todoitem;
@end

完整源代码https://github.com/martylavender/LittleToDoApp/tree/Storyboards

编辑

在Fennelouski的评论之后,我应该有一些类似的东西吗?

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [self.toDoItem removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
        [self.tableView reloadData];
    }
}

编辑2

这是我得到的:

https://www.evernote.com/l/AJiah58lVhdGXIYO1F5yv6fJXc7k3WjRLNYB/image.png

表中的行数是[[ToDoItemSvc retrieveAllToDoItems] count]。当您删除表中的1行时,那么表中的行数应该是1小于删除任何行之前的行数。在删除1行并调用[self.tableView reloadData]之后,tableView检查表中有多少行。此时,numberOfRowsInSection将返回[[ToDoItemSvc retrieveAllToDoItems] count]。现在,它应该比删除行之前的1小。

简短的回答是,您需要首先从数据源中删除一个项目,它似乎是[ToDoItemSvc retrieveAllToDoItems],然后删除一行。

值得称赞的是,当您添加一行时,您还需要向数据源添加一个项。

这些更改需要在调用reloadData之前发生。

<标题>编辑
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    // Actually remove the data from the source
    [ToDoItemSvc deleteToDoItem:[ToDoItemSvc retrieveAllToDoItems][indexPath.row]]
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView reloadData];
}
老师有五个学生:Alice, Bob, Charlie, Diane和Eric。鲍勃的妈妈在午饭前很早就去学校接他。午饭后,老师点名,惊慌失措,因为他只有四个孩子,而名单上说应该有五个。鲍勃在哪里? !如果Bob的妈妈在带他离开学校的时候把他的名字从名单上去掉,那么老师就不会惊慌了。

在上面的帮助和一些思考下,我想出了这个问题。

首先,我完成了实际的deleteToDoItem代码
- (ToDoItem *) deleteToDoItem: (ToDoItem *) todoitem {
    [ToDoItems removeObject:todoitem];
    return todoitem;
}

则上面的代码

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    ToDoItem *toDoItem = [[ToDoItemSvc retrieveAllToDoItems] objectAtIndex:indexPath.row];
    [ToDoItemSvc deleteToDoItem:toDoItem];
    [self.tableView reloadData];
    NSLog(@"Removing data");
}

这允许我像我想要的那样删除我的项目!

最新更新