ios 错误“无效更新:第 0 节中的行数无效”试图删除表中的行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30516970/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:06:47  来源:igfitidea点击:

Error 'Invalid update: invalid number of rows in section 0' attempting to delete row in table

iosobjective-cuitableview

提问by Marty

My code appears to run just fine but when I swipe to delete a line within my UITableView, the app crashes with the following:

我的代码似乎运行得很好,但是当我滑动以删除 UITableView 中的一行时,应用程序崩溃并显示以下内容:

Error

错误

LittleToDoApp[70390:4116002] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

LittleToDoApp[70390:4116002] *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效。更新后现有节中包含的行数 (1) 必须为等于更新前该部分中包含的行数 (1),加上或减去从该部分插入或删除的行数(0 插入,1 删除),加上或减去移入或移出的行数那个部分(0 移入,0 移出)。

ViewController.m

视图控制器.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

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

Full source

完整来源

https://github.com/martylavender/LittleToDoApp/tree/Storyboards

https://github.com/martylavender/LittleToDoApp/tree/Storyboards

Edit

编辑

Following up after the comment/s made by Fennelouski, should I have something along these lines?

在 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];

    }
}

Edit 2

编辑 2

This is what I am getting:

这就是我得到的:

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

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

回答by Fennelouski

The number of rows in your table is [[ToDoItemSvc retrieveAllToDoItems] count]. When you delete 1row in your table, then the number of rows in your table should be 1less than the number of rows before deleting any rows. After you delete 1row and call [self.tableView reloadData]the tableView checks to see how many rows there are in the table. At this point, numberOfRowsInSectionwill return [[ToDoItemSvc retrieveAllToDoItems] count]. This should now be 1less than it was before you deleted a row.

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

The short answer is, you need to first remove an item from your dataSource, which appears to be [ToDoItemSvc retrieveAllToDoItems]then delete a row.

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

The compliment to this is when you add a row, you need to add an item to your dataSource as well.

对此的赞美是当您添加一行时,您还需要向数据源添加一个项目。

These changes need to happen before you call reloadData.

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

Edit

编辑

- (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];
}

ELI5: A teacher has five students: Alice, Bob, Charlie, Diane, and Eric. Bob's mom picks him up early from school before lunch. After lunch, the teacher takes attendance and panics because he only has four kids when the list says there should be five. Where's Bob?!

ELI5:一位老师有五个学生:Alice、Bob、Charlie、Diane 和 Eric。鲍勃的妈妈早早在午饭前去学校接他。午饭后,老师出勤并惊慌失措,因为当名单上说应该有五个孩子时,他只有四个孩子。鲍勃在哪里?!

If Bob's mom had removed his name from the list when she took him out of school then the teacher wouldn't have panicked.

如果鲍勃的妈妈在带他离开学校时将他的名字从名单中删除,那么老师就不会惊慌失措。

回答by Marty

I figured it out with the help from above and some thinking.

我在上面的帮助和一些思考下弄清楚了。

First, I finished the actual deleteToDoItem code

首先,我完成了实际的deleteToDoItem代码

- (ToDoItem *) deleteToDoItem: (ToDoItem *) todoitem {

    [ToDoItems removeObject:todoitem];

    return todoitem;
}

Then the code above

然后上面的代码

- (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");
}

This runs and allows me to delete my item like I want!!

这会运行并允许我根据需要删除我的项目!!