I am using a fetched results controller, which, it seems wants to set up using sections in my UITableView. Well, in this case, I don't want to use sections. Which, is easy enough to set the value for numberOfSectionsInTableView: to 1. But, now I am not sure how to get the numberOfRowsInSection: to return all of the cells into one section.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
How can I make that method return all the cells, since I only want to use 1 section?
In order for your fetched results controller to avoid returning objects in sections, you need to properly initialize it as follows:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
Here there will be no sections because you pass nil as the argument of sectionNameKeyPath. Then, your tableView:numberOfRowsInSection method is correct.
Even though you don't want to use sections, and initialize the fetchedResultsController with sectionNameKeyPath= nil, this fetchedResultsController is still populated with ONE section and all of its items. This is how Apple implement the fetchedResultsController.
So in your case, you have 1 section at the index 0, and in this section, you have all the items, starting from index 0 to the numberOfRowsInSection-1.
Hope it helps.
Related
Here is my problem. I have a table view that is filled depending on the content of an array. Up to now, everything is fine. This array is used to store some maps name coming from a server. I have a refresh button so that when I click on it, it will update the array with the new maps name (refresh method).
When I click on the refresh button, the maps array is modified. Either some maps are added or deleted (if a user deleted a map or created a new map for example). However, this situation does not reflect on the screen.
Let's say I have 5 maps (named 1,2,3,4,5). If I delete one (let's say the map 3) and call refresh, the maps array (mapsModel->mapsNameList) will contain 4 maps and the content of this array is proper. However, on the iphone screen, I would see, in the table view, (1,2,4,5,5). I don't know why it doesn't remove a row if it is not in the maps array anymore.
I get the same problem if I try to add a map (let's say a map 0), I would get (0,1,2,3,4) and the number 5 would not be there.
If I restart the application, then all the maps appear properly...
Here is my code, if some variables name aren't clear of obvious, please let me know !
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int numberOfRows = [[MapsModel sharedMapsModel] numberOfMapsInSection:((UITabBarController*) self.parentViewController).tabBar.selectedItem.tag];
// Return the number of rows in the section. THIS FUNCTION WORKS
return numberOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MapsModel* mapsModel = [MapsModel sharedMapsModel];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[[cell textLabel] setText:[[mapsModel->mapsNameList objectAtIndex:((UITabBarController*) self.parentViewController).tabBar.selectedItem.tag] objectAtIndex:indexPath.row]];
return cell;
}
-(void) refresh {
[[MapsModel sharedMapsModel] populateMapsNameListWithMapState:MAP_STATE_ALL];
[self updateView];
}
-(void) updateView {
//Reorder the maps by alphabetical order
NSSortDescriptor *sortDescriptor;
MapsModel* mapsModel = [MapsModel sharedMapsModel];
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[[mapsModel->mapsNameList objectAtIndex:self.tabBarItem.tag] sortUsingDescriptors:sortDescriptors];
//Update the table view
[self.tableView reloadData];
}
Are you sure that [[MapsModel sharedMapsModel] numberOfMapsInSection: returns correct value after update? Your table code has no problem, it seems that you should check your MapsModel.
Probably your mapsModel returns wrong values in cellForRowAtIndexPath: and numberOfRowsInSection:
I know the problem, I had a tab bar controller and three table views. However, I tried to use only one view controller for all the three table views... One view controller per table view resolved my problem !
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:YES];
[self.tableView endUpdates];
This piece of code crashes my iOS application and returns the following error:
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 (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).
Can someone tell me why? And how come it says that the number of rows after the update is zero, when it at the same time says 1 inserted, 0 deleted?
Update
The code above gets triggered when I receive a push notification during runtime. When that happens, the delegate adds the received data to a dictionary stored in a .plist file (which the table view uses to populate its cells with) followed by a call to a custom method in the RootViewController, where the table view is located. This method executes the code above followed by a crash. However, when I log [theDictionary count] in - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section the number increments just as it should right before crashing.
Also worth noting is that when i log self.tableview I receive a different "memory address" from the RootViewController when it loads upon launch, and when receiving a push notification. Maybe this has something to do with it?
This is my - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [theDictionary count];
}
And a picture of the error:
The numberOfRowsInSection method should return the correct number of rows whenever you make any change to the data source of table view.
For example if the total number of rows in the table is 5 and if you are going to add one more row, then the numberOfRowsInSection method should return 6.
Its easily achieved by having the data in an array or dictionary. If you want to add or delete rows just update the array or dictionary. And just return [array count] or [dictionary count] from numberOfRowsInSection method.
I have a couple of UIViewControllers that show a table view of my 'User' objects as the section, and 'Site' objects as rows under each section. The Site to User relationship is one-to-many.
At the moment I am showing this data with the following code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:0];
return [sectionInfo numberOfObjects];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
User *sectionUser = (User*)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:section inSection:0]];
return [[sectionUser.sitesToUser allObjects] count];
}
In my NSFetchResultsController I'm just pulling all User objects, nothing special.
Now my issue - whenever I update the results, by removing a section (ie a User) or adding a User whilst this view is also open, I get an NSFetchedResultsController delegate error:
Serious application error. An exception was caught from the delegate of
NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid
update: invalid number of sections. The number of sections contained in the table
view after the update (2) must be equal to the number of sections contained in the
table view before the update (1), plus or minus the number of sections inserted or
deleted (0 inserted, 0 deleted). with userInfo (null)
I only get it in the views where I am displaying my data in this way, is User is section, Site/s are rows under each section.
Am I doing something really stupid? I thought it would just remove the section when I delete it etc... Seems to work fine if each User is a ROW in the UITableView - which I believe is the crux of my issue...
I'm using Core Data but my code for numberOfSectionsInTableView: and tableView:numberOfRowsInSection: method is different... I don't know if this can solve your problem but, try to use this code:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rowsNumber = 0;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
rowsNumber = [sectionInfo numberOfObjects];
return rowsNumber;
}
For the sections, I've never used it but try to work on the sectionNameKeyPath argument of the initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName: when you initialize your controller.
You haven't shown your fetchedResultsController delegate calls:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
- (void)controller:didChangeObject:atIndexPath:forChangeType:newIndexPath
- (void)controller:didChangeSection:sectionInfoAtIndex:forChangeType:
These calls are responsible for adding and removing the rows / sections from your table so everything "adds up" as far as the tableview is concerned.
In your case you are kind of "abusing" what a section is so this code could get tricky.
If you are prepared to do without animation you could just implement:
-(void)controllerDidChangeContent:(NSFetchedResultsController*) controller {
[self.tableView reloadData];
}
Daniel.
I have two different table views in which I use the exact same code in tableView:MoveRowAtIndexPath:ToIndexPath: to support user reordering of the rows. One of these tableViews works perfectly. The other one, however, gets confused and starts displaying the same subview no matter which row is selected - i.e. its row indexing seems to have got messed up.
I've temporarily fixed this by adding a [tableView reloadData] at the end of theMoveRowAtIndexPath method, but I don't understand why it wasn't working in the first place - especially since another view with the exact same code works perfectly. Obviously, there must be another method in this view controller which is messing it up, but I don't know where to look.
Here is the code that is the same in both:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
userDrivenDataModelChange = YES;
NSMutableArray *things = [[fetchedResultsController fetchedObjects] mutableCopy];
NSManagedObject *thing = [[self fetchedResultsController] objectAtIndexPath:fromIndexPath];
[things removeObject:thing];
[things insertObject:thing atIndex:[toIndexPath row]];
int i = 0;
for (NSManagedObject *mo in things)
{
[mo setValue:[NSNumber numberWithInt:i++] forKey:#"displayOrder"];
}
[things release], things = nil;
[managedObjectContext save:nil];
userDrivenDataModelChange = NO;
}
(For what it's worth, the one that works is the child view of the one that doesn't, and they are in a to-many Core Data relationship).
Use a different identifier for each tableviews in
(UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
If you use the same identifier string, they will pickup each other's cached cell objects and show inconsistent data.
I forgot that I was using a custom cell on the parent view, and that the user presses a UIButton within the cell, rather than the cell itself. The custom cell has its index path set as a property by tableView:cellForRowAtIndexPath:, which of course never gets called again after the table cells are moved. Hence, I had to add a [tableView reloadData] to make sure the cells get updated.
I am currently developing an app to show fixtures in a football competition using data held in NSArrays. I would like to now put the data into section i.e. for example Round 1 showing 3 matches, Round 2 Showing 6 matches etc.
Can someone please assist me with how I can show the data in a UITable in sections?
Regards
John
I usually use NSArrays in a NSArray for this.
This is how I would create the datasource array:
NSArray *section0 = [NSArray arrayWithObjects:#"Section 0 Row 0", #"Section 0 Row 1", nil];
NSArray *section1 = [NSArray arrayWithObjects:#"Section 1 Row 0", #"Section 1 Row 1", #"Section 1 Row 2", nil];
self.tableViewData = [NSArray arrayWithObjects:section0, section1, nil];
and some UITableView methods so you get the idea:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.tableViewData count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.tableViewData objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyObject *object = [[self.tableViewData objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
// Do something
}
And for the titles (and indexes) of the sections I create separate NSArrays.
better to create Array of Array . suppose you have 3 sections and every section have 4 rows . so create one array with three array object .and each inner array will contains the rows value
You need to create a class that conforms to the UITableViewDataSource protocol. This class is typically a subclass of UIViewController. You should then set the dataSource property of a UITableView to point to an instance of this class. Your data source class should implement the method sin the protocol so as it returns values based in the data in your model (hence this is a controller class). If it helps, you can also use the UIViewController subclass UITableViewController.