Deleting the last section of a UITableView causes an exception - ios

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView internal bug: unable to generate a new section map with old section count: 1 and new section count: 0'
Below is the code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#try {
NSMutableArray *arrData = [dictStartDate* objectForKey:self.navigationItem.title];
NSLog(#"Title : %#", self.navigationItem.title);
NSLog(#"Array count : %i", (int)arrData.count);
return arrData.count;
}
#catch (NSException *ex) {
[[ProjectHandler sharedHandler]LogException:ex.name description:ex.description className:#"TodayTasksViewController" functionName:#"numberOfRowsInSection"];
return 0;
}
}

I had this problem and I solved it testing if I am deleting the last row of a section, and if so instead of deleting the row, I deleted the section. I am returning 0 to the numberOfSectionsInTableView and there is no problem, the tableView is just empty.
Bellow follows the swift code I used to check if it is the last row of a section or not and the key part, which is to delete the section and not the row.
Suppose you have objects for sections in a table view like this:
var objects: [[Object]]?
Then if you are removing a row of this table with this objects you should do:
objects![indexPath.section].removeAtIndex(indexPath.row)
if (objects![indexPath.section].count == 0) {
objects!.removeAtIndex(indexPath.section)
tableView.deleteSections(NSIndexSet.init(index: indexPath.section), withRowAnimation: .Left)
return //End the func and not execute the next step
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Left)
Also you could use something like this to identify if it's the last row in a section:
tableView.numberOfRowsInSection(indexPath.section)

You seem to be deleting the only section in the table. A table must have at least 1 section. You might want to check your code to check that you are not returning 0 in numberOfSectionsInTableView:

Related

Invalid update: invalid number of sections

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (3) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted).'
but i insert 1 and deleted one based on data source what i missed
self.states?.append(sortedStates) //Update state property
if (self.states?.count)! > 3 {
self.states?.removeFirst()
}
self.newsFeedTableView.beginUpdates()
self.newsFeedTableView.insertSections([(self.states?.count)! - 1], with: .none)
if (self.states?.count)! > 3 {
let statesForoldestStateTime = self.states?.first
self.newestStateTime = statesForoldestStateTime?.first?.createdAt
let indexpostion = (self.states?.count)! - 3
self.newsFeedTableView.deleteSections([indexpostion], with: UITableViewRowAnimation.none)
}
self.newsFeedTableView.endUpdates()
The error says it all. When if (self.states?.count)! > 3 is false. The only section would be inserted and not deleted.
You should update your data source accordingly. The number of sections method must return someArray.count. When you insert some section, make sure to update that some array, and when you delete some section, delete the element from some array. That will resolve the issue.

How to implement NSFetchedResultsController delegate when multiple row is on deletion?

Two items is deleted.
func controller(controller: NSFetchedResultsController!, didChangeObject anObject: AnyObject!, atIndexPath indexPath: NSIndexPath!, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath!) {
switch(type) {
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
default:
break
}
}
At deletion line I get this error:
CoreData: error: Serious application error. Exception was caught
during Core Data change processing. This is usually a bug within an
observer of NSManagedObjectContextObjectsDidChangeNotification.
Invalid update: invalid number of rows in section 0. The number of
rows contained in an existing section after the update (0) must be
equal to the number of rows contained in that section before the
update (2), plus or minus the number of rows inserted or deleted from
that section (0 inserted, 1 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out). with
userInfo (null)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 0. The number of rows contained in an
existing section after the update (0) must be equal to the number of
rows contained in that section before the update (2), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
You need to 'batch' the updates by also implementing the controllerWillChangeContent and controllerDidChangeContent delegate methods as follows (although sorry these are Obj-c):
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}

Assertion failure in UICollectionView when trying to reload data

I get an Error:
Assertion failure in -[UICollectionViewData numberOfItemsBeforeSection:],/SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionViewData.m
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for number of items before section 2 when there are only 1 sections in the collection view'
when i try to reload the collectionview.It works when new sections are added but when i try to remove them it throws an exception.
update:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return object.count;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
if([sortString isEqualToString:#"title"])
return [Obj.ordered allKeys].count;
else
return 1;
}
Before deleting rows or sections, you have to delete items from your data source array. If your method returns 1 before deleting section then after deleting it should return 0.

May I map n records to 1 table view row with NSFetchedResultsController?

In my application, I use Core Data as well as NSFetchedResultsController. I need to use 1 table view row to display 9 records instead of 1 record. So I customise the numberOfRowsInSection
method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [self numberOfRowsForNumber: [sectionInfo numberOfObjects]];
}
- (NSInteger)numberOfRowsForNumber:(NSInteger)num
{
NSInteger a = num / 9;
NSInteger b = num % 9;
return a + (b == 0 ? 0 : 1);
}
Adding the first record is OK. But when I add the second record, there is an error:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2903.23/UITableView.m:1330
2013-11-13 06:54:56.217 TestNSFetchedResultsController[2693:a0b] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
Does it mean NSFetchedResultsController support 1 record to 1 row mapping only? Or is there better way to accomplish it?
This is easy to do with the TLIndexPathController class from TLIndexPathTools if you're OK with using an NSFetchedResultsController alternative. It rolls all five NSFetchedResultsController delegate methods into a single didUpdateDataModel method, so it is generally easier to work with. But more to your needs, it provides a willUpdateDataModel delegate method that lets you map your fetched managed objects into an arbitrary data model. Here is an example mapping the objects into groups of nine:
- (TLIndexPathDataModel *)controller:(TLIndexPathController *)controller willUpdateDataModel:(TLIndexPathDataModel *)oldDataModel withDataModel:(TLIndexPathDataModel *)updatedDataModel
{
NSArray *ungroupedItems = updatedDataModel.items;
NSMutableArray *groupedItems = [NSMutableArray array];
NSMutableArray *group;
for (id object in ungroupedItems) {
if (!group) {
group = [NSMutableArray arrayWithCapacity:9];
}
[group addObject:object];
if (group.count == 9 || object == [ungroupedItems lastObject]) {
[groupedItems addObject:group];
group = nil;
}
}
return [[TLIndexPathDataModel alloc] initWithItems:groupedItems];
}
Without going into too much detail about how TLIndexPathTools works (see the readme), you write your view controller code against the TLIndexPathDataModel API. So when you do the above mapping, your view controller just needs to be aware that the items in the data model are arrays of managed objects.
There are a couple of sample projects worth checking out:
The "Core Data" project demonstrates the Core Data integration
The "No Results" project demonstrates using the above delegate method to display a "no results" row anytime the data model contains zero items.
The one caveat is that TLIndexPathTools was only designed to scale to a few thousand items. So if you've got a really big data set, you might run into performance issues.
I figured it out. I just customise methods of NSFetchedResultsControllerDelegate. For example, I define customised NSFetchedResultsChangeInsert, NSFetchedResultsChangeDelete cases of didChangeObject method.
Thank #Duncan Groenewald for your reply.

NSRangeException - NSArrayM objectAtIndex

EDIT - SOLVED: the problem was when I created the string with all the objects to put into the tableview. I used the '?' to separe each object and one of those contained that character so the array broke.
[myString appendFormat:#"%#$%#$%#$%#?",
[currentCampeggioDict valueForKey:#"id"],
[currentCampeggioDict valueForKey:#"nome"], [...]
NS Array *elencoCampeggi=[myString componentsSeparatedByString:#"?"];
That's it.
I have this problem: my app receives from an URL a XML file with a list of objects. The tableview is populated by those objects. There are so many items, so I have to call several times the url, changing a parameter in it for example :
http://www.myurl.com?method=xxx&PAGE=1
http://www.myurl.com?method=xxx&PAGE=2
etc..
The last cell of the tableview contains the sentence "Click to load next 25 objects" and it changes the page number for the URL, delete itself and reload the url, adding the new objects to the tableview.
After 4 times everything works great, but when I press the cell for the fifth, an exception appears with the following text:
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 17 beyond bounds [0 .. 16]'
Any help? Thanks.
EDIT: some code: this is the part where I delete the last cell
-(void) loadTestData {
// fill moviesArray with test data
NSInteger lastRow = [CampeggiArray count]-1;
if (lastLoadedPage > 1){
[CampeggiArray removeObjectAtIndex:lastRow];
[self.tableView reloadData];
}
[...]
Issue is because you are returning wrong row count in your tableview delegate method. You need to identify the total no of objects to be listed in the tableview and return the count in your tableview delegate method. This is not your page size.
For each hit you need to keep on appending the new objects to the already existing datasource(NSArray) and reload the table.There will be no objects in the datasource during initial hit.
I guess, you are returning 16 instead of 15 in the delegate method, so you wil get crash with exception. Don't hard code values in your delegate method, just get the total number of objects(entire set) and return the count of it.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [totalObjects count];
}

Resources