Deleting rows manually from UITableView in Xcode 6 - objective-c

I have the following code written for my delete button to delete selected rows from the UITableView.
-(IBAction)deleteItems:(id)sender {
NSArray *selectedCells = [self.autoCompleteView indexPathsForSelectedRows];
NSMutableIndexSet *indicesToDelete = [[NSMutableIndexSet alloc] init];
for (NSIndexPath *indexPath in selectedCells) {
[indicesToDelete addIndex:indexPath.row];
}
//arrayFromPlist is NSMutableArray
[autoCompleteView beginUpdates];
[autoCompleteView deleteRowsAtIndexPaths:selectedCells withRowAnimation:UITableViewRowAnimationAutomatic];
[autoCompleteView endUpdates];
[selectedObjects removeObjectsAtIndexes:indicesToDelete];
[autoCompleteView reloadData];
[alertMsg deleteConfirmation:#"Do you want to delete these items?"];
}
Please check the image for my UITableView and the delete button. I have kept the Editing property of my UITableview to "Multiple Selection during Editing" in the storyboard.
I get the following error when i press the Delete button shown in the screen.
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 (5) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 2 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Not sure what is my mistake in the delete button code.

One definite problem is that you are removing the cells before you have removed the corresponding data from the model. That's wrong. Always change the model first; then change the cells in such a way as to match the model.

Related

Unable to simultaneously satisfy constraints - UIScrollViewAutomaticContentSizeConstraint

I recently added constraints to my prototype cell on a uitableview view and have run in to this warning when running my application (no errors but see this exception in the console log). I am adding objects to a mutable array dynamically based on specific conditions and inserting to the top of my uitableview. The insert rows also have variable heights.
The issue doesn't occur when I initialize an array with predetermined value, only when I add objects dynamically and push new view controllers.
I understand uitableview contains a scroll view and I have constrained the uitableview, but for some reason the error below says I have a scroll view constraint issue. I am not quite sure what it means.
All of my constraints were created using storyboard on Xcode 5. Dynamically changing height or the cell is done programmatically.
<_UIScrollViewAutomaticContentSizeConstraint:0x17597a10 UITableView:0x18416000.contentHeight{id: 79} == -110.000000>"
)
Will attempt to recover by breaking constraint
<_UIScrollViewAutomaticContentSizeConstraint:0x17597a10 UITableView:0x18416000.contentHeight{id: 79} == -110.000000>
I had a similar error. I my case I solved it by returning UITableViewAutomaticDimension for the estimated row height, instead of a fixed number:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
I found the solution here: Autolayout Error with UITableView
Same error with same circumstances, when i changed an entry of the MutableArray (_detailColumns). The error only appeared once, not at following changes!
I had included the Update in
[self.tableView beginUpdates];
[[_detailColumns objectAtIndex:tag]setValue:textViewText forKey:#"content"];
[self.tableView endUpdates];
After removing beginUpdates and endUpdates, the message was gone (?!)

Dynamically updating sections of UITableView without using reloadData

First off I sincerely apologize if this has been answered. I have been battling with this for too long. I would appreciate any kind of help.
I am developing an application that gets data asynchronously from different web sources and parses it into objects that are handled by my main Model. This model provides information for a UITableView. When data is received and parsed, i need to modify the number of sections and rows in the table view. I first receive the data that decides the number of sections, and then asynchronously data that modifies the number of rows in each section and their content. I have the table views data sources set up.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.sijainti.pysakit count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//if ([[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count] == 0) return 1;
NSLog(#"ajatStr of section: %d count: %d",section, [[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count]);
return [[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count];
}
The object names are in Finnish (long story). The second line of numberOfRowsInSection was commented to ease debugging. Now, because the data is dynamic i need to update the interface when i have data ready for display, or the user wont know about it until they scroll away and back again. I have a method for this:
- (void)updateRowWithCell:(SCPysakki *)valmisPysakki {
if (!valmisPysakki.isInArray) return;
int section = [self.sijainti.pysakit indexOfObject:valmisPysakki];
[self.tableView beginUpdates];
NSIndexSet *sectionSet = [NSIndexSet indexSetWithIndex:section];
[self.tableView reloadSections:sectionSet withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
This is called only after the number of sections is known. I do NOT want to use reloadData due to efficiency, and animation. I used to have this set up so that i had only rows and was just drawing rows inside of the rows. But once the rows became taller than the iphone screen, it became very unclear, because you couldn't see the title. So i made them sections, and the rows drawn real rows. Then i was suddenly faced with a problem. reloadRowsAtIndexPaths: withRowAnimation: worked absolutely fine but using reloadSections: withRowAnimation: gave me this exception:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-09-17 04:11:27.769 pysappi[2351:f803] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. 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 (0), 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 was able to eliminate this by adding the [self.tableView endUpdate] and [self.tableView endUpdate] to the update method. But oh no, that only works if I:
Use reloadData after receiving the data deciding the number of sections (which in turn shortens the tableview so that after the whole update the user at the top of the table after every single update. I would rather update the sections dynamically, without using reloadData, except when removing Sections, but if I try to do this i get the exception again.)
update the sections after receiving data for only one Row (so if I try to update all the rows in the section once I've received all their data, I get the exception again.)
Now this creates a situation where creating a good user interface becomes impossible. I'm convinced that the underlying problem is not solved with the start and end updating calls, but that just fixes it for some very very odd reason.
I also tried adding the rows manually with insertRowsAtIndexPaths: and updating them manually also, but this returns the exact same error. During a long debugging progress i have been able to determine that this exception rises after cellForRowAtIndexPath: gets called and it returns a cell for one of the new rows that was added during the updating, even though the numberOfRowsInSection should have updated the amount of rows. I also suppose that if those few circumstances apply this is why the start and end updating methods make the row updating work.
The only thing that has worked outside of what i explained above, is using only reloadData, but that is just practically impossible considering the amount of updating.
This became a lot longer than i thought, and its 4.30 AM. Not the first time I'm up this late thanks to this exception. If you need more information ask. Thank you in advance.
(Answered in the comments. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
#deleted_user wrote:
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 (0), - you had a section with zero rows and did an update with a row - cocoa didnt see that as an insert and failed you

Obj-C iOS Delete UITableViewCell

Im trying to delete a UITableViewCell from a table. I am having a problem because sometimes the cell that I want to delete has been scrolled off the screen and potentially recycled. I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
The delete method works perfectly if the deleted cell is visible in the tableview.
Does anyone know how to solve this?
Thanks
the best way to delete a cell is to delete first the item in your data source (NSmutableArray) and then delete the cell or reload your uitableview
[yourArray removeObjectAtIndex:yourIndex];
[yourTableView reloadData];
Surely you populate your UITableView with data, right? This data most likely comes from a container class like an NSArray or something like that, I presume. Merely delete the item of data from the NSArray and update the number of rows in section with the new number of items. Don't try to delete the UITableViewCell if it is off-screen.

Incorrect indexPath against UITableView after calling reloadData from textFieldDidEndEditing

So I'm trying to emulate the way in which the standard Apple code works on the contacts app when managing telephone numbers. Specifically here, I'm working on deleting a row from a tableview if its empty and the user navigates to any other row
My problem is that as the tableview reload causes the UITextField to resign its responder, I need to set the responder again for the text field navigated to by the user
I have the UITextField delegate and am handling the usual textFieldShouldBeginEditing , textFieldDidBeginEditing , textFieldShouldEndEditing , textFieldDidEndEditing
In order to handle the functionality, my code is within textFieldDidEndEditing, whereby I remove the data from the tableview array, and as the tableview has 2 sections, I am calling:
[MyTableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
During textFieldDidBeginEditing I save the indexPath of the textField being edited using:
EditableCustomCell *textFieldCell = (EditableCustomCell *)[[textField superview] superview];
NSIndexPath *indexPath = [MyTableView indexPathForCell:(EditableCustomCell *)textFieldCell];
responderIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
I then use the following code to set the correct rows textField to be the first responder:
EditableCustomCell *customCell = (EditableCustomCell *)[MyTableView cellForRowAtIndexPath:responderIndexPath];
[customCell.editableTextField becomeFirstResponder];
Everything seems fine until near the end of the processing, when all of a sudden textFieldDidBeginEditing starts to return section 0 row 0 for the indexPath (even though when examining the tag value or text contained return the correct values for the textfield)
Below is a log from the start of the process explained above:
- textFieldDidEndEditing started <-- start of delete processing
- textFieldDidEndEditing - tableviewData - replacing object at index 1
CustomTableViewController.deleteRow - delete called for indexPath section 1 ... row 1
- reloading MyTableView
- CustomTableViewController-cellForRowAtIndexPath started
CustomTableViewController-cellForRowAtIndexPath section=1 row=0
CustomTableViewController-cellForRowAtIndexPath ending
CustomTableViewController-cellForRowAtIndexPath section=1 row=1
CustomTableViewController-cellForRowAtIndexPath ending
CustomTableViewController-cellForRowAtIndexPath section=1 row=2
CustomTableViewController-cellForRowAtIndexPath ending
- textFieldShouldBeginEditing started
indexPath for textFieldShouldBeginEditing is : section 1 row 1
- textFieldShouldBeginEditing ending
- textFieldDidBeginEditing started
indexPath for textFieldDidBeginEditing is : section 1 row 1 text 3 tag 1
- textFieldDidBeginEditing ending
- textFieldDidEndEditing ending <-- end of delete processing
- textFieldDidBeginEditing started
- textFieldDidBeginEditing ... setting responderIndexPath section 0 row 0
indexPath for textFieldDidBeginEditing is : section 0 row 0 text 123 tag 0
- textFieldDidBeginEditing ending
As can be seen from the last part of the log, after textFieldDidEndEditing completes, textFieldDidBeginEditing is called but returns section 0 and row 0 (the rows stay displayed and visible throughout)
I can neither understand why this is called, or why it is not returning the correct indexPath. As can be seen, the text is returned for the value (in this case the entered value of 123) and I have verified this with other data and other rows (for both text and tag for the textfield)
Perhaps my setting of becomeFirstReponsder within textFieldDidEndEditing is incorrect, although if that's true, I'm at a loss as to where to process this
Hoping someone out there with a better understanding of this can help me out, as you can tell, I've been through this for several hours without any resolution
Thanks
Izzy
EDIT 1: In the code, all that is called is becomeFirstReponder before textFieldDidEndEditing completes. When you look at the log after cellForRowAtIndexPath has completed, it enters and exists the textfield TWICE, once for the row that was below the row just removed, and then again when it returns 0 for the section/row of the indexPath ? I am failing to understand what sequence of events is causing this post table reload firing of methods
EDIT 2: Is it just me, or does it seem really strange that there is NO textfieldSHOULDbeginEditing prior to the textFieldDidBeginEditing at the end of the log ? Is it that I am screwing with the internal process by reloading the table during textFieldDidEndEditing ? Is there a better place to do this (ie. remove a row and reload the tableview to show the UI update) ?
OK, to answer my own question ...
After alot of debugging, it seems that ...
I'm inteferring with the standard UI workflow by reloading the tableview during textfieldDidEndEditing
Calling reloaddata destroys the tableview cells (and all objects contained within the cells ie. UITextFields, UILabels, etc) and recreates them, causing
By moving this call out to a standalone button, it functioned as expected without the indexPath returning 0 (I still dont fully understand the call stack when it gets to this point, but hey)
Now my BIG problem is deciding when to programatically call the reload, as I want it driven off the event of leaving the text field ... I feel another question coming on :\
Hope my ramblings help someone out in the future that tries to do a similar thing ...

NSInternalInconsistencyException when trying to dynamically modify number of rows in UITableView

I am attempting to use insertRowsAtIndexPaths so I can insert a table row. I am getting this error:
*** 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 (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).'
What I am trying to do is to dynamically modify the number of rows in section in my attempt at rectifying the problem:
NSArray *indexPathIndex = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil];
[myTableView beginUpdates];
//***** HERE I AM TRYING TO DYNAMICALLY INCREASE NUMBER OF ROWS
[myTableView numberOfRowsInSection:1];
////////////////////////////////////////////////////////////
[myTableView insertRowsAtIndexPaths:indexPathIndex withRowAnimation:UITableViewRowAnimationTop];
[myTableView endUpdates];
Initially, my number of rows in section is 0. I try to increase this number in my efforts to address the aforementioned error however the code [myTableView numberOfRowsInSection:1] seems to have no effect.
Could somebody please suggest how I can fix this problem?
Thank you,
Victor.
You should not be setting the number of rows yourself, the UITableView manages that on its own. Remove the line.
myTableView numberOfRowsInSection:1];
Take a look at the UITableViewDataSource protocol, particularly the tableView:numberOfRowsInSection method. This will allow you to dynamically set the number of rows in the section.
A tableView has a datasource that is generally an array, that you can use to fill the cells of the tableView.
- (UITableViewCell*)tableView cellForRowAtIndexPath..... {
//Dequeueing or alloc-init-ing a cell
SomeClass *someObject = [someArray objectAtIndex:indexPath.row]; // Something
// like this, where someArray becomes the source array.
...
}
Now in some other part of your code, if you want to dynamically change the contents of the tableView, change the someArray and then change the tableView.
[someArray insertObjectAtIndex:someIndex];
[myTableView insertRowsAtIndexPaths....]; //The indexPath is generally
// consistent with the someIndex.