UITableView reloadData with empty array - ios

I am deleting all the items in UITableView. And thus the array from which I am loading the UITableView has count = 0. After deleting the last item in array, on reloading the table, I am getting error at numberofRowInSection.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrProjects count];
}
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"");
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[tableProject beginUpdates];
Project *project = [arrProjects objectAtIndex:indexPath.row];
[[CommonModel shared]DeleteProjectDetails:project.ProjectId];
[arrProjects removeObject:project];
[self reloadTableProject:YES];
[tableProject endUpdates];
}
}
-(void) reloadTableProject:(BOOL)isReloadRequired
{
//[arrProjects removeAllObjects];
arrProjects = [[CommonModel shared]GetAllProjects];
if(isReloadRequired)
[tableProject reloadData];
}
This is the error :
'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 (2), plus or minus the number of rows inserted or deleted from
that section (0 inserted, 0 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out).'
I am getting this error every time, not only when the array is empty.

Set :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [_datasourceArray count];
}
To empty your TableView, do :
[_datasourceArray removeAllObjects];
[_tableView reloadData];

You need to remove the object from arrProjects too, inside UITableViewCellEditingStyleDelete,
[arrProjects removeObjectAtIndex:indexPath.row];
it is also good to include
[self.tableView beginUpdates] and [self.tableView endUpdates]
when you start and end the removal of these objects

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrProjects count];
}
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"");
if (editingStyle == UITableViewCellEditingStyleDelete)
{
Project *project = [arrProjects objectAtIndex:indexPath.row];
[[CommonModel shared]DeleteProjectDetails:project.ProjectId];
[arrProjects removeObject:project.ProjectId];
[self reloadTableProject: YES];
}
}

Related

UITableView deleteRowsAtIndexPaths crash

I have UITableView and I made implementation for -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath :
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSUInteger beforeDeleteCount = historyArray.count;
VideoItem *video = [historyArray objectAtIndex:indexPath.row];
[[HistoryRepository sharedHistory] removeFromHistories:video];
if (self.titleSort) {
[self sortArrayByTitleAtoZWithReloadData:NO];
} else {
[self sortArrayByNormalWithReloadData:NO];
}
NSUInteger afterDeleteCount = historyArray.count;
if (beforeDeleteCount == afterDeleteCount) {
[table reloadData];
} else {
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
}
-(void)sortArrayByNormalWithReloadData:(BOOL)reload {
self.titleSort = NO;
historyArray = [[NSMutableArray alloc] initWithArray:[[HistoryRepository sharedHistory] historyArray]];
if (reload) {
[self setTableHeader];
[table reloadData];
}
}
-(void)sortArrayByTitleAtoZWithReloadData:(BOOL)reload {
self.titleSort = YES;
NSSortDescriptor * sortDescriptor = [[[NSSortDescriptor alloc]initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)] autorelease];
historyArray = [[NSMutableArray alloc] initWithArray:[[[HistoryRepository sharedHistory] historyArray] sortedArrayUsingDescriptors:#[sortDescriptor]]];
if (reload) {
[self setTableHeader];
[table reloadData];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [historyArray count];
}
And I still get this kind of error all the time:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (27) must be equal to the number of rows contained in that section before the update (27), 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).
I always making a check if it's the same count so reload the table and don't delete but still i'm getting this error.
Reordering the data source array after deleting an item makes no sense if the array is already sorted.
And you must not call deleteRowsAtIndexPaths after reordering the data source array anyway.
This version of commitEditingStyle: is sufficient
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
VideoItem *video = [historyArray objectAtIndex:indexPath.row];
[[HistoryRepository sharedHistory] removeFromHistories:video];
[historyArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

How to reload custom table view cell after last row deletion?

I have a UITableView with a simple array as data source. I designed a custom nib for a special UITableViewCell that is shown when the array is empty (i.e. when the app first starts).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([self.wallet count] == 0)
return 1;
else return [self.wallet count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// If there isn't any coupon yet, then show the custom "No Coupon Yet" cell
if ([self.wallet count] == 0)
return [tableView dequeueReusableCellWithIdentifier:#"NoCouponCell" forIndexPath:indexPath];
else {
CouponCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CouponCell" forIndexPath:indexPath];
Coupon *coupon = [self.wallet objectAtIndex:indexPath.row];
[cell configureForCoupon:coupon];
return cell;
}
}
Since I'd like to add the swipe-to-delete functionality, I provided the following method for the view controller.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
[self.wallet removeObjectAtIndex:indexPath.row];
// Creates a new, temporary array holding just the one index-path item
NSMutableArray *tmp = [[NSMutableArray alloc] initWithObjects:indexPath, nil];
// Tells the table view to delete the row with a nice animation
[tableView deleteRowsAtIndexPaths:tmp withRowAnimation:UITableViewRowAnimationAutomatic];
}
However, when the table view has only one row and I try to delete it, my app crashes. Why?
EDIT: The debug info tells that a NSInternalInconsistencyException is raised.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source
[self.wallet removeObjectAtIndex:indexPath.row];
[tableView reloadData]; // reload your table to see updates
// or if you what some animation use|
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
...
Here's a fix for your cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CouponCell *cell = [tableView dequeueReusableCellWithIdentifier:[self.wallet count] == 0 ? #"NoCouponCell" : #"CouponCell" forIndexPath:indexPath];
if ([self.wallet count] > 0)
{
Coupon *coupon = [self.wallet objectAtIndex:indexPath.row];
[cell configureForCoupon:coupon];
}
return cell;
}
try this in your code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.wallet count];
}
You are returning a rowcount of 1 if your data array wallet has no elements:
if ([self.wallet count] == 0)
return 1;
else return [self.wallet count];
But in the commitEditingStyle method you don't handle this case: This means that it's possible to delete the NoCouponCell. And I assume that the crash occurs, because you want to remove an object from your data-array which does not exist.
[self.wallet removeObjectAtIndex:indexPath.row];
Solution: Use the following delegate method to determine when a cell can be edited: This prevents the delegate method commitEditingStyle to be called.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return [self.wallet count] > 0
}
Try this piece of code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
[self.wallet removeObjectAtIndex:indexPath.row];
// Creates a new, temporary array holding just the one index-path item
NSMutableArray *tmp = [[NSMutableArray alloc] initWithObjects:indexPath, nil];
// Tells the table view to delete the row with a nice animation
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:tmp withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}
Use this in your numberOfRowsInSection function:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.wallet count];
}
And at the end of your commitEditingStyle function, use
[tableView reloadData];
you can add a view to the UITableview tableFooterView on tableview datasource, you can put your custome cell'content in this view
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (dataArray.count == 0) {
UIView *view = [[UITableView alloc] initWithFrame:self.view.bounds];
view.backgroundColor = [UIColor blueColor];
self.tableView.tableFooterView = view;
}
return dataArray.count;
}

UITableView - Controlling or preventing the delete for a specific row in a section

I am trying to prevent the delete for every last row in every section of my UITableView control. I wrote the code which is working so far.
Is there a way to prevent the delete button from appearing for a specific row in a specific section when in edit mode of UITableView?
Here is the code :
- (void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = indexPath.section;
NSInteger row = indexPath.row;
// If table view is asking to commit a delete command...
if ( editingStyle == UITableViewCellEditingStyleDelete) {
// Prevent deleting the last row
int length = [[[MyItemStore sharedStore] getItemsForGivenSection:section] count];
// PREVENT LAST ROW FROM DELETING
if ( row == length) {
return;
}
NSArray *items = [[MyItemStore sharedStore] getItemsForGivenSection:section];
MyItem *item = items[row];
[[MyItemStore sharedStore] removeItemFromSection:item fromSection:section];
//Also remove that row from Table view with animation
[tableView deleteRowsAtIndexPaths: #[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
You could use something like this:
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
//Return FALSE for the last row
if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1)
return FALSE;
}
//Return TRUE for all other rows
return TRUE;
}
You can do this but tableView:commitEditingStyle:forRowAtIndexPath is too late as this gets called when the user already triggered the deletion. You can either return UITableViewCellEditingStyleNone from tableView:editingStyleForRowAtIndexPath: or set the editingStyle property of the UITableViewCell. Sounds like you want to go with the former and return none if it's the last row in each section.
You can use this code in the Table View Controller:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1)
return UITableViewCellEditingStyleNone;
}
return UITableViewCellEditingStyleDelete;
}

invalid number of rows in section 0

I have a thread problem SIGABRT when i try to add one more cell in my app; That is the exception which Xcode gave me:
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) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
That is the code where i consider is the problem
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PlayerCell *cell = (PlayerCell *)[tableView
dequeueReusableCellWithIdentifier:#"PlayerCell"];
Player *player = [self.players objectAtIndex:indexPath.row];
cell.nameLabel.text = player.name;
cell.gameLabel.text = player.game;
cell.ratingImageView.image = [self
imageForRating:player.rating];
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.players removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade]; }
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [self.players count];
}
UPD.
I have an array in AppDelegate to preconfig several cells (to show user, how it looks like) and full it with follow code:
_players = [NSMutableArray arrayWithCapacity:20]; // make an array in appdelegate
Player *player = [[Player alloc] init]; //
player.name = #"Bill Evans"; //
player.game = #"Tic-Tac-Toe"; //Make new player
player.rating = 4; //
[_players addObject:player]; //Add in array in AppDelegate
PlayersViewController *playersViewController = [PlayersViewController new]; //Make instance for main array
playersViewController.players = _players; //add new item from appDelegate array to main
Another piece of code where the mistake may be (it loads when user taps on save button to add new cell)
- (void)playerDetailsViewController:
(PlayerDetailsViewController *)controller
didAddPlayer:(Player *)player
{
[self.players addObject:player];
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow:[self.players count] - 1 inSection:0];
[self.tableView insertRowsAtIndexPaths:
[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
}
So can anyone tell me, where is my mistake? If needed, i can send further code.
Thanks.

copy row to another section in UITableView

I have a tableView with with some sections and each section with some rows.Every row has a favorite button.If the button is clicked the row should get added to the favourites section.
(which is initially empty).
I have written some code.but the problem is it is working in iOS 5 simulator and getting crashed in iOS 6 simulator with Invalid tableView update error.
Can someone tell me where the problem is.?
-(void)moveRowToFavourites:(id)sender{
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:[NSIndexPath indexPathForRow:favouritesArray.count inSection:0]];
[favouritesArray insertObject:cell.textLabel.text atIndex:favouritesArray.count];
[[self tableView] beginUpdates];
[[self tableView] insertRowsAtIndexPaths:(NSArray *)tempArray withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}
EDIT
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(searching){
return [self.searchResults count];
}
else{
if ([[arrayForBool objectAtIndex:section] boolValue]) {
if(section == 0){
return favouritesArray.count;
}else{
NSString* key = [self.proCategoriesArray objectAtIndex:section - 1];
NSArray *aArray = [sectionContentDict valueForKey:key];
return aArray.count;
}
}
return 1;
}
}
You just should call the following:
[favouritesArray addObject:cell.textLabel.text];
[tableView reloadData];
Make sure you implemented this function:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
switch(section){
case 0:
return favouritesArray.count;
//Your other sections have to placed here, too
default:
return 0;
}
}
Then your tableView should update itself. The problem is that you insert whole cells, but you just have to insert data in your array. Hope it helps!
It seems like your data source is returning 0 as number of sections and rows. You are not correctly inserting/deleting rows, when you insert a new row, data source methods get called again, and if for instance you try to insert an object in a way that the row count will be 4, and data source tableView:numberOfRowsInSection: returns 3, you get an exception because you're trying yo add more objects that what data source promises.
When you try to update a table view all these data source methods get called again:
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView;
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath;
You must ensure that they return correct values. I suspect that you aren't even implementing them.
Edit
The problem is that you are just inserting a new object in favouritesArray, but this doesn't affect the number of rows returned by tableView:numberOfRowsInSection:. There you aren't returning favouritesArray.count, the exception is due to the fact that tableView:numberOfRowsInSection: returns a lower number than the rows that the table view should have.
In your case I think that you don't even need to call insertRowsAtIndexPaths:, you can do everything with your data source:
-(void)moveRowToFavourites:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
[favouritesArray insertObject:cell.textLabel.text atIndex:favouritesArray.count];
[[self tableView]reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return favouritesArray.count;
}
In tableView:cellForRowAtIndexPath: you should return a cell that has an object fetched form favouritesArray as text.

Resources