混合静态和动态UITableViewController内容会导致NSRangeException

CRE*_*8IT 5 iphone uitableview ipad ios nsrangeexception

我一直在搜索这个错误,并找到一些具有类似行为的帖子,但没有解决问题的解决方案.

我有一个UITableViewController(在SB中声明为Static),它有章节:Section 0(Recipe)是静态的4个单元格,Section 1(Flavors)应该是动态的

故事板截图

这是我用来测试的代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    rows = [[NSMutableArray alloc] initWithArray:@[@"test",@"test",@"test",@"test",@"test",@"test",@"test"]];
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
    switch (section) {
        case 0:
        return 4;
        break;
        case 1:
        return rows.count;
        break;

        default:
        break;
    }

    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell;

    switch (indexPath.section) {
        case 0:
        return [super tableView:tableView cellForRowAtIndexPath:indexPath];
        break;

        case 1:

        cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        if (!cell)
        {
// create a cell
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
        }
        cell.textLabel.text = [rows objectAtIndex:indexPath.row];
        return cell;

        default:
        break;
    }

    return cell;
}
Run Code Online (Sandbox Code Playgroud)

现在我在运行时遇到的错误是:'NSRangeException',原因:'*** - [__ NSArrayI objectAtIndex:]:索引1超出边界[0 .. 0]'

我知道我想念一些小东西,有人能帮助我吗?

非常感谢

Mat*_*uch 9

实际问题是tableViewControllers动态和静态的工作方式.

如果创建动态UITableViewController,则大多数方法返回nil或0或其他默认值.因此,如果您不在UITableViewController子类中覆盖它们,则不会发生任何不良情况.

如果您创建一个静态UITableViewController,大多数方法会"询问"故事板要返回的内容.实际上,可能有一些私有数组作为后备存储,包含所有必要的数据.如果不覆盖查询此后备存储的方法,则默认实现将向数组请求不存在的索引处的对象.


你的问题是你告诉tableView它有2个部分和几行.因此tableView会询问静态UITableViewController,例如第二部分第二行中单元格的高度.但是这个indexPath不存在于静态tableView使用的后备存储中(因为您只将一行放入第二部分).

因此,如果tableView想要访问静态表存储中不存在的信息,则必须实现一些dataSource和delegate方法并返回自己的值.

如果查看异常的调用堆栈,您应该能够看到这些方法.

例如:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
    0   CoreFoundation    0x000000010ebaaf35 __exceptionPreprocess + 165
    1   libobjc.A.dylib   0x000000010e843bb7 objc_exception_throw + 45
    2   CoreFoundation    0x000000010eaa301e -[__NSArrayI objectAtIndex:] + 190
    3   UIKit             0x000000010f4dc856 -[UITableViewDataSource tableView:heightForRowAtIndexPath:] + 109
    4   UIKit             0x000000010f21026b __66-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]_block_invoke + 302
    5   UIKit             0x000000010f20f8fe -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 4125
    6   UIKit             0x000000010f214e45 -[UITableViewRowData rectForFooterInSection:heightCanBeGuessed:] + 320
    7   UIKit             0x000000010f214f3a -[UITableViewRowData heightForTable] + 56
Run Code Online (Sandbox Code Playgroud)

在调用堆栈的索引3处,您可以看到-[UITableViewDataSource tableView:heightForRowAtIndexPath:]已调用objectAtIndex:引发异常的代码.如果您不阻止它们执行此操作,这是将其调用转发到后端存储的方法之一.所以你必须实现这个方法并返回一些有用的东西.然后你继续,直到没有更多例外.

需要多少方法可能取决于tableView的配置.我实现了通常会导致这些异常的四个,所以如果你看到更多的异常是由未覆盖的UITableViewDataSource/Delegate方法引起的,你可以看到你应该遵循的模式:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    CGFloat height = 0;
    if (section == 0) {
        // static section
        height = [super tableView:tableView heightForHeaderInSection:section];
    }
    return height;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    CGFloat height = 0;
    if (section == 0) {
        // static section
        height = [super tableView:tableView heightForFooterInSection:section];
    }
    return height;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat height = 44;
    if (indexPath.section == 0) {
        // static section
        height = [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
    return height;
}

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger indentationLevel = 0;
    if (indexPath.section == 0) {
        // static section
        indentationLevel = [super tableView:tableView indentationLevelForRowAtIndexPath:indexPath];
    }
    return indentationLevel;
}
Run Code Online (Sandbox Code Playgroud)

这里有一个小技巧可以让您的静态内容更加独立于您的代码:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section == 0) {
        // static section
        return [super tableView:tableView numberOfRowsInSection:section];
    }
    return self.objects.count;
}
Run Code Online (Sandbox Code Playgroud)

如果混合动态和静态单元格,调用super将变得非常有用.