Mixing static and dynamic contents of a UITableViewController raises an NSRangeException

I searched for this error and found several posts with similar behavior, but not a solution that solves the problem.

I have a UITableViewController (declared as Static in SB) that has sections: Section 0 (Recipe) is 4-cell static, Section 1 (Flavors) should be dynamic

Storyboard screenshots

This is the code I use for testing:

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

Now the error I get when I start it is: "NSRangeException", the reason is: "*** - [__ NSArrayI objectAtIndex:]: index 1 outside the bounds [0 .. 0] '

I know that something is bothering me, can someone help me?

Many thanks

+5
source share
4 answers

The real problem is how the dynamic and static tableViewControllers work.

If you create a dynamic UITableViewController, most methods return nil or 0 or another default value. Therefore, nothing bad happens if you do not overwrite them in a subclass of UITableViewController.

If you create a static UITableViewController, most methods "ask" the storyboard what needs to be returned. In fact, it is likely something like a private array as a backup storage that contains all the necessary data. And if you do not overwrite the methods that request this backup storage, the default implementation will request an array for objects in indexes that do not exist.


Your problem is that you tell tableView that it has 2 sections and several rows. Thus, tableView queries the static UITableViewController about things like cell height in the second row in the second section. But this indexPath does not exist in the backup storage (because you only put one row in the second section), which is used by the static table view.

So, you need to implement a couple of dataSource methods and delegate and return your own values ​​if tableView wants to access information that does not exist in the storage of static tables.

If you look at the stack of exception calls, you can see these methods.

For instance:

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

In index 3 of the call stack, you can see that -[UITableViewDataSource tableView:heightForRowAtIndexPath:] called the code objectAtIndex: which caused the exception. This is one of the methods that transfers its calls to the backend store if you do not stop making them. Therefore, you must implement this method and return something useful. And then you continue until there are no more exceptions.

How many methods are required may depend on the configuration of your TableView. I have implemented four that usually throw these exceptions, so you can see the pattern that should be followed if you see more exceptions thrown by UITableViewDataSource / Delegate methods that are not overwritten:

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

and here is a little trick to make your static content even more independent of your code:

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

If you mix dynamic and static cell call super it becomes very useful.

+6
source

This behavior does not seem to be trivial. You may need to override all methods using NSIndexPath . You can read this discussion and this one for more information.

+1
source

So it seems that I found the cause of this error. Now the question is whether I solved it correctly or is there another way to do it programmatically ...

My solution: In SB, select the section that should be dynamic, and specify the number of maximum rows that it will contain. In fact, in my case, I do not want the user to add more than 30 tastes to the recipe, so I provided 30 lines for this section.

When launching the application, my array has only 7 objects, although I set the lines to 30 for the section, it only displays 7 defined in the array and does not display the other 23.

The reason for this error is that when you want to mix static and dynamic partitions, you must determine that your table is static for work. In static mode and with SB, it seems that the code reads the number of cells that you have in SB for the partition. If this number is less than your dataset, it uses an NSRangeException ...

So far this is my solution, since I know that I will not have more than 30 cells, if someone knows how to fix this problem more dynamically, let us know

0
source

I replaced the super call with UITableViewAutomaticDimension for row height and added a delegate method to evaluate row height. In iOS 9, I had problems with non-expandable strings that automatically expanded in iOS8. My static cells were also dynamic heights, and dynamic cells were fixed heights.

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

}

0
source

Source: https://habr.com/ru/post/1208240/


All Articles