2014-12-03 72 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; 

}