2014-12-03 68 views
5

我一直在搜索这个错误,并找到一些类似的行为,但没有解决方案,解决了这个问题。混合静态和动态UITableViewController内容导致NSRangeException

我有一个UITableViewController(声明为静态在SB),其具有以部分:第一部分0(配方)是静态的4个细胞,第1(香料)应该是动态

Storyboard Screenshot

这是

- (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:]:指数1超出范围[0 .. 0]'

我知道我错过了某个地方的小东西,有人可以帮助我吗?

非常感谢

+0

看看这个http://stackoverflow.com/a/9428324/285190 – Flexicoder 2014-12-03 14:05:25

+0

我怎么没看到你的链接应该在帮助这种情况下,因为这里描述的问题不等于我的,除了我使用UITableViewController而不是UIViewController – CRE8IT 2014-12-03 14:16:30

+0

它崩溃之后numberOfRowsInSection甚至没有进入CellForRowAtIndexPath – CRE8IT 2014-12-03 14:41:22

回答

6

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

如果您创建动态UITableViewController,大多数方法返回零或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 

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

需要多少个方法取决于你的tableView的配置。我实现了四个通常导致这些例外,所以你可以看到,如果你看到更多的由不被覆盖UITableViewDataSource /委托方法引起的异常,你应该遵循的模式:

- (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变得非常有用。

+0

除了显示一个可行的解决方案,我现在了解整个过程的复杂性,感谢您的解释..我会尽快尝试并报告 – CRE8IT 2014-12-03 16:17:20

+0

这个解决方案像一个魅力一样工作,我选择这个答案是最有帮助的,因为它也解释了关于异常堆栈的过程的背景......非常感谢! – CRE8IT 2014-12-03 17:00:55

0

所以看起来我发现这个错误发生的原因。现在的问题是我是否正确地解决了它,或者是否有另一种编程方式...

我的解决方案是:在SB中,选择应该是动态的部分并提供它将包含的最大行数。本身,在我的情况下,我不想让用户为配方添加超过30种口味,所以我为此部分提供了30行。

当运行该应用程序,我的阵列仅具有7对象,虽然我设置的行30的部分中,它仅使所述阵列中规定的7,并且不显示其它23.

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

现在这是我的解决方案,因为我知道我不会有超过30个单元,如果有人知道如何解决这个问题更具活力的方式,请让我们知道

1

看来这种行为并不平凡。您可能需要用NSIndexPath覆盖所有方法。你可以阅读这discussionthis one欲知更多信息。

+0

我错过了Apple上的一个线程,这似乎很有帮助......我会尽快尝试 – CRE8IT 2014-12-03 16:09:01

0

我用UITableViewAutomaticDimension替换了超级调用的行高,并为估计的行高添加了委托方法。在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; 

}