【问题标题】:Mixing static and dynamic UITableViewController content causes NSRangeException混合静态和动态 UITableViewController 内容会导致 NSRangeException
【发布时间】:2015-02-01 02:56:56
【问题描述】:

我一直在寻找这个错误,并确实找到了一些具有类似行为但没有解决问题的解决方案的帖子。

我有一个 UITableViewController(在 SB 中声明为静态),它必须包含部分:第 0 部分(配方)是静态的,有 4 个单元格,第 1 部分(风味)应该是动态的

这是我用来测试的代码:

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

现在我在运行时遇到的错误是:'NSRangeException',原因:'*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

我知道我在某个地方错过了一些小事,有人可以帮助我吗?

非常感谢

【问题讨论】:

  • 我看不出你的链接在这种情况下应该如何提供帮助,因为那里描述的问题不等于我的问题,除了我使用的是 UITableViewController 而不是 UIViewController
  • 它在 numberOfRowsInSection 之后立即崩溃,甚至没有进入 CellForRowAtIndexPath
  • 您基本上必须实现几乎所有 UITableViewDataSource 和 UITableViewDelegate 方法,调整 indexPath 参数并使用调整后的 indexPath 调用 super 或返回您自己的值。根据我的经验,混合静态和动态细胞很快就会变得很痛苦。您应该看看只使用动态是否有意义。
  • @MatthiasBauch 我尝试只使用动态,但由于静态单元格都会有所不同,这让我的生活更加困难

标签: ios iphone ipad uitableview nsrangeexception


【解决方案1】:

实际的问题是动态和静态 tableViewController 的工作方式。

如果你创建一个动态 UITableViewController,大多数方法返回 nil 或 0 或其他默认值。因此,如果您不在 UITableViewController 子类中覆盖它们,就不会发生任何不好的事情。

如果您创建一个静态 UITableViewController,大多数方法都会“询问”情节提要返回什么。实际上,可能有类似私有数组的东西作为包含所有必要数据的后备存储。如果您不覆盖查询此后备存储的方法,则默认实现将向数组询问不存在索引处的对象。


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

因此,如果 tableView 想要访问静态表存储中不存在的信息,您必须实现几个 dataSource 和委托方法并返回您自己的值。

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

例如:

*** 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

在调用堆栈的索引 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;
}

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

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

如果您混合使用动态和静态单元格,调用super 会非常有用。

【讨论】:

  • 除了给出一个可行的解决方案之外,由于您的解释,我现在了解了整个过程的复杂性。我会尽快尝试并报告
  • 这个解决方案就像一个魅力,我选择这个答案是最有帮助的,因为它还解释了关于异常堆栈的过程背景......非常感谢!
【解决方案2】:

似乎这种行为并非微不足道。您可能需要使用NSIndexPath 覆盖所有方法。您可以阅读此discussionthis one 了解更多信息。

【讨论】:

  • 我错过了Apple上的一个线程,这似乎有帮助......我会尽快尝试
【解决方案3】:

看来我找到了发生此错误的原因。现在的问题是我是否正确解决了它,或者是否有另一种编程方式......

我的解决方案是:在 SB 中,选择应该是动态的部分并提供它将包含的最大行数。就我而言,就我而言,我不想让用户在一个食谱中添加超过 30 种口味,因此我为此部分提供了 30 行。

在运行应用程序时,我的数组只有 7 个对象,虽然我将部分的行数设置为 30,但它只呈现数组中定义的 7 个,而不显示其他 23 个。

发生该错误的原因似乎是当您想混合静态和动态部分时,您必须将表定义为静态才能工作。在静态模式和 SB 中,代码似乎读取了 SB 中某个部分的单元格数。如果这个数字小于你的数据数组,它会抛出一个 NSRangeException...

目前这是我的解决方案,因为我知道我不会有超过 30 个单元格,如果有人知道如何以更动态的方式解决此问题,请告诉我们

【讨论】:

    【解决方案4】:

    我用 UITableViewAutomaticDimension 替换了对 super 的调用以获取行高,并添加了用于估计行高的委托方法。在 iOS 9 中,我遇到了在 iOS8 中自动扩展的行不扩展的问题。我的静态单元格也是动态高度,动态单元格是固定高度。

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

    }

    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 44;
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      • 2021-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多