UITableView reload section - iphone

I want to reload only one section not the full table. Is there any method in UITableView.
[tableView reloadData] is used to load full table.
I want to know how to load only one section, as I have large number of rows in the table.

The reloadSections method bugs me -- as I have to construct a few objects. This is great if you need the flexibility, but sometimes I also just want the simplicity too. It goes like this:
NSRange range = NSMakeRange(0, 1);
NSIndexSet *section = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:section withRowAnimation:UITableViewRowAnimationNone];
This will reload the first section. I prefer to have a category on UITableView and just call this method:
[self.tableView reloadSectionDU:0 withRowAnimation:UITableViewRowAnimationNone];
My category method looks like this:
#implementation UITableView (DUExtensions)
- (void) reloadSectionDU:(NSInteger)section withRowAnimation:(UITableViewRowAnimation)rowAnimation {
NSRange range = NSMakeRange(section, 1);
NSIndexSet *sectionToReload = [NSIndexSet indexSetWithIndexesInRange:range];
[self reloadSections:sectionToReload withRowAnimation:rowAnimation];
}

Yes, there is:
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation

But you can reload only sections which contain same number of rows (or you have to manually add or remove them). Otherwise you will get:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 2. 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).'
Which is not required when you use [tableView reloadData].
When you need to reload a section and you have changed number of rows inside it, you could use something like this:
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section];
[self beginUpdates];
[self deleteSections:indexSet withRowAnimation:rowAnimation];
[self insertSections:indexSet withRowAnimation:rowAnimation];
[self endUpdates];
If you put it in a category (like bandejapaisa shows) it could look like this:
- (void)reloadSection:(NSInteger)section withRowAnimation:(UITableViewRowAnimation)rowAnimation {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section];
[self beginUpdates];
[self deleteSections:indexSet withRowAnimation:rowAnimation];
[self insertSections:indexSet withRowAnimation:rowAnimation];
[self endUpdates];
}

For Swift 3, 4 and 5
let sectionToReload = 1
let indexSet: IndexSet = [sectionToReload]
self.tableView.reloadSections(indexSet, with: .automatic)

that the correct way:
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];

Based on the accepted answer here, I made a function that will reload all sections in the table using an animation. This could probably be optimized by reloading only visible sections.
[self.tableView reloadData];
NSRange range = NSMakeRange(0, [self numberOfSectionsInTableView:self.tableView]);
NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade];
In my case, I had to force a reloadData before the section animation, because the underlying data for the table had changed. It animates properly however.

You need this... For Reload Row
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
or For Reload section
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation

Here is the method, you can pass section details in different ways
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:1] withRowAnimation:NO];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
Reloading particular sections improves performance for the table view as well some time it also avoid some issues like floating/moving custom headers-footers in your view. SO try to use reloadSection than relaodData whenever possible

Try to use
[self.tableView beginUpdates];
[self.tableView endUpdates];
Hope this will solve your issue.

But you can reload only sections which contain same number of rows (or you have to manually add or remove them). Otherwise you will get an NSInternalInconsistencyException.
Steps:
calculate which rows to remove and/or insert
generate an IndexPath array from these
call related tableView methods
now you can safely call reloadSections :) Reload section will call update for the rest of the indexes.
Or you can use a library like : https://github.com/onmyway133/DeepDiff
Swift pseodo code:
tableView.deleteRows(at: valueIndexesToRemove, with: .automatic)
tableView.insertRows(at: valueIndexesToInsert, with: .automatic)
tableView.reloadSections(IndexSet([section]), with: .automatic)

If you have custom section view you can add a weak reference to it in your view controller and update it whenever you want. Here is my code for reference:
#property (weak, nonatomic) UILabel *tableHeaderLabel;
....
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UITableViewHeaderFooterView *myHeader = [[UITableViewHeaderFooterView alloc] init];
UILabel *titleLabel = [[UILabel alloc] init];
[titleLabel setFrame:CGRectMake(20, 0, 280, 20)];
[titleLabel setTextAlignment:NSTextAlignmentRight];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setFont:[UIFont systemFontOfSize:12]];
[myHeader addSubview:titleLabel];
self.tableHeaderLabel = titleLabel; //save reference so we can update the header later
return myHeader;
}
Then later on you can update your section like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
self.tableHeaderLabel.text = [NSString stringWithFormat:#"Showing row: %ld", indexPath.row];
}

Related

Change an image in a UITableViewCell without reloading cell

I'm writing an iPhone app with a UITableView as the primary user interface. Each section consists of two rows, a header and the body. When the user clicks on the header, I remove the second row by changing the numberOfRowsInSection value:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
cbwComponent *comp = [_componentController objectInListAtIndex:section];
if([comp hasContentsView] && !comp.contentsHidden){
return 2;
}else
return 1;
}
When the user selects the header, I'm using the following code:
comp.contentsHidden = YES;
[self.tableView beginUpdates];
NSArray *deleteIndexPaths = [[NSArray alloc] initWithObjects:[NSIndexPath indexPathForRow:1 inSection:indexPath.section], nil];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
It's working great, with a nice smooth fade effect. The problem is, I'm trying to add an indicator in the header cell (row 0) that changes when it's clicked on. To change that image I have to refresh the top row as well as the second row, which makes the transition look bad (well, not nearly as smooth). Is there a way to change the image in a UITableViewCell without refreshing the cell?
Thanks
EDIT: I figured it out! You can maintain the smooth transition as long as you reload that first row before you make the change to the second row. It has to be called inside of [tableView beginUpdates];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[[NSArray alloc] initWithObjects:[NSIndexPath indexPathForRow:0 inSection:indexPath.section], nil] withRowAnimation:UITableViewRowAnimationNone];
...
[self.tableView endUpdates];
Did the trick.
You could also subclass a tableview cell and implement a view transition in it that can be called from your view controller. You could then call that without having to reload the cell.
[(YourCustomCell*)[tableView cellForRowAtIndexPath:indexPathOfYourCell] fadeInIndicator];

Strange animation when moving last row out of section and deleting section

I have a multi-section tableview. In edit mode I allow rows to be moved from one section to another. Once the final row is removed from one section I delete that section. So I am using deleteSection inside moveRowAtIndexPath.
When the final item is moved from the section, the section header disappears as planned. But there is a very strange animation bug, where the moved row seems to 'merge' with the row it is dropped above, and an empty row is displayed at the bottom of the 'to' section (probably because the numberOfRows for that section is correct, but 2 rows are in the same position). Even stranger, when I click the reorder control for this row (not moving the item, simply touching and releasing), the two items 'unmerge'.
I have posted a video demonstrating this.
I have tried wrapping my data changes and view changes in begin/end updates, but to no avail.
I have uploaded a test project here, and I will also post the code below. A couple of points:
I have tried to replicate my data source's format in the demo project, in case this is where the problem originates. The key thing is that my source is a composite array of two other arrays (though I can't see why this would be an issue).
To see the behavior in question, move the two rows in the bottom section, up into the top section. Don't drop them in the last row on the top section though, since this seems to work ok.
Moving rows the other way, from the top section to the bottom section, is buggy in this demo project.
Code (all of this is in the demo project):
I set up my arrays in loadView:
- (void)loadView{
array1 = [NSMutableArray array];
[array1 addObject:#"test 0"];
[array1 addObject:#"test 1"];
[array1 addObject:#"test 2"];
array2 = [NSMutableArray array];
[array2 addObject:#"test a"];
[array2 addObject:#"test b"];
[super loadView];
}
I also have a method that returns a combination of these arrays - this is used as the data source:
- (NSMutableArray *)sourceArray{
NSMutableArray *result = [NSMutableArray array];
if (array1.count > 0) {
[result addObject:array1];
}
if (array2.count >0) {
[result addObject:array2];
}
return result;
}
Which allows for very simple number of rows/sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.sourceArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[self.sourceArray objectAtIndex:section] count];
}
Standard Cell/Header formatting:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [[self.sourceArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [NSString stringWithFormat:#"Section %i", section];
}
This is where I do the magic
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
I notice that the row that comes from the to-be-deleted section is the one that disappears until you retouch the order control.
I suspect that when this datasource method is called by the tableview, its state is still in the middle of performing the move, so calling 'deleteSections' will make the table try and delete the row you're moving. It's not so much of a merge as the fact that it's fading away at the same rate as the section header, and the one below it is just scooting back up to fill the space.
Tapping the control causes the table view to rejigger itself and realize that the row isn't actually gone.
to try and work around this, try running the deletion in the next runloop, via a dispatch call, like:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
dispatch_async(dispatch_get_main_queue(), ^() {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
});
}
this will cause the deletion to run on the main thread still, but allow the 'moveRow' and whatever call stack it happens to be in finish up its logic before the deletion call
Your problem is in the animation. One is being done while another is not yet finished (moving & deleting animation) causing one cell to be drawn upon the other. You can verify this by moving the cells around again. The correct order will then be displayed. According to Apple's docs on the UITableView:
Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method.
Therefore to fix this, do this to your code:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(someMethod:) withObject:fromIndexPath afterDelay:1.0];
}
- (void) someMethod:(NSIndexPath *) fromIndexPath {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
Should work fine. Just change the delay to something shorter that suites you.
On the off chance that your rows or what's inside them can take focus, have you checked that you have called resignFirstResponder or [view endEditing:YES]? We saw this when we used text fields and (IIRC it was iOS 4 version dependent too) left the focus in one of the fields.
You have to reload the tableview after deleting the section. Try this code.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}
}
Swap the order of fromArray and toArray in your code. If the item has a retain count of 1 prior to removing it from the array, it will have a retain count of 0 before adding it to toArray.
If you swap the order, the item will go from retain count of 1 to 2 then back to 1 when the remove is complete.
I think the UITableViewRowAnimationFade animation is interfering with the UITableViewCell move animation. One thing you can try is to delay the section deletion a little bit late in order for the cell move row animation to finish.
Try replace your code with the following code.
-(void)deleteSection:(NSIndexSet*)indexSet
{
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(deleteSection:) withObject:[NSIndexSet indexSetWithIndex:fromIndexPath.section] afterDelay:1.0];
// [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
a solution that lost animation on last row :
if([listOfItemsOnTransaction count]==indexPath.row){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
}

Adding a cell in UITableView with animation

I have a UITableView. I'm population it from a NSDictionary with arrays for each set of items on the table: labels, footers, Headers, UIViews, etc.
In section 0, I want a row #2 appear when a switch in row #1 is switched to on.
What I have done and it works is, in numberOfRowsInSection I added this code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (interruptor.isOn==NO && section==0) {
return [[[infoTableContentArray objectAtIndex: section] objectForKey:kLabelKey] count]-1;
}else{
return [[[infoTableContentArray objectAtIndex: section] objectForKey:kLabelKey] count];
}
}
and the action linked to the switch (interruptor) is:
-(IBAction)accioInterruptor:(id)sender{
[infoAndSettingsTable reloadData];
}
so when the switch is switched, the table reloads and the cell appears or disappears.
it actually works, but there is no animation, which makes it, mhh... well, you know.
I've tried to implement the reloadRowsAtIndexPaths:withRowAnimation, adding it to the code called by the switch:
-(IBAction)accioInterruptor:(id)sender{
[infoAndSettingsTable beginUpdates];
[infoAndSettingsTable reloadRowsAtIndexPaths:[[infoTableContentArray objectAtIndex: 0] objectForKey:kLabelKey] withRowAnimation:UITableViewRowAnimationBottom];
[infoAndSettingsTable endUpdates];
}
But, it dowsn't work. It crashed on the line [infoAndSettingsTable endsUpdates];
BTW, in all the cases the following:
[[infoTableContentArray objectAtIndex: 0]
is the array which contains the labels for that section.
Am I doing it right or I'm Epic-Failing alltogether?
Thanks in advance!
simple way to do this......
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
insertIndexPaths is an array of NSIndexPaths to be inserted to your table.
deleteIndexPaths is a array of NSIndexPaths to be deleted from your table.
Example array format for index paths :
NSArray *insertIndexPaths = [[NSArray alloc] initWithObjects:
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0],
[NSIndexPath indexPathForRow:2 inSection:0],
nil];
got it from this question
the argument to reloadRowsAtIndexPaths: should be an array of NSIndexPath objects identifying the rows you want to reload, not the labels for that section. Also, looks like you want to reload a section so I would try the following:
-(IBAction)accioInterruptor:(id)sender {
[self reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationBottom];
}
Why don't you just use UITableView's insertRowsAtIndexPaths:withRowAnimation:? It has been built exactly for this purpose, the UITableView class reference has the exact description and usage examples.
Apart from being cleaner it is also more performant since you don't have to reload the entire table (only really matters if you have lots of cells in it though)

how to delete a row in a section and hiding the section?

I am having an issue where when i have two sections let say section x and section. Section x has one row and also section y. If i delete a row in section y which is the second one, it will delete the row in section x and keeping the row in section y also it leave a space when i hide the section if their is a way to hide the section which wont make a mess please tell me a way here are some picture that might let u understand my question
and here is the code i use to delete a row
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
int path;
path = indexPath.row;
[data removeObjectAtIndex:path];
if([data count] == 0)
{
UIBarButtonItem *leftbutton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
self.navigationItem.rightBarButtonItem = leftbutton;
[leftbutton release];
[self.navigationItem.rightBarButtonItem setEnabled:FALSE];
[mainTableView setHidden:TRUE];
}
else
{
[self.navigationItem.rightBarButtonItem setEnabled:TRUE];
[mainTableView setHidden:FALSE];
}
[self saveData];
[mainTableView reloadData];
}
To remove a section when you have deleted all rows belonging to it, call - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation & return one less from - (NSInteger)numberOfSections.
You haven't added any code that shows how you are deleting a row from the table (removing it from data isn't sufficient). Try something like-
[self.tableView beginUpdates];
[data removeObjectAtIndex:path];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
Well at the beginning i wasn't able to solve the issue but with the help of #Akshay my first problem was while deleting it always delete the section on top the section i was deleting to solve the issue i just had to update the number of rows in a section using
[mainTableView reloadData]; before the return expression

reloadRowsAtIndexPaths:withRowAnimation: crashes my app

I got a strange problem with my UITableView: I use reloadRowsAtIndexPaths:withRowAnimation: to reload some specific rows, but the app crashes with an seemingly unrelated exception: NSInternalInconsistencyException - Attempt to delete more rows than exist in section.
My code looks like follows:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
When I replace that reloadRowsAtIndexPaths:withRowAnimation: message with a simple reloadData, it works perfectly.
Any ideas?
The problem is that you probably changed the number of items of your UITableView's data source. For example, you have added or removed some elements from/to the array or dictionary used in your implementation of the UITableViewDataSource protocol.
In that case, when you call reloadData, your UITableView is completely reloaded including the number of sections and the number of rows.
But when you call reloadRowsAtIndexPaths:withRowAnimation: these parameters are not reloaded. That causes the next problem: when you are trying to reload some cell, the UITableView checks the size of the datasource and sees that it has been changed. That results in a crash. This method can be used only when you want to reload the content view of the cell (for example, label has changed or you want to change its size).
Now if you want to remove/add cells from/to a UITableView you should use next approach:
Inform the UITableView that its size will be changed by calling method beginUpdates.
Inform about inserting new row(s) using method - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation.
Inform about removing row(s) using method - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation.
Inform the UITableView that its size has been changed by calling the method endUpdates.
I think the following code might work:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
I had this problem which was being caused by a block calling reloadRowsAtIndexPaths:withRowAnimation: and a parallel thread calling reloadData.
The crash was due to reloadRowsAtIndexPaths:withRowAnimation finding an empty table even though I'd sanity checked numberOfRowsInSection & numberOfSections.
I took the attitude that I don't really care if it causes an exception. A visual corruption I could live with as a user of the App than have the whole app crash out.
Here's my solution to this which I'm happy to share and would welcome constructive criticism. If there's a better solution I'm keen to hear it?
- (void) safeCellUpdate: (NSUInteger) section withRow : (NSUInteger) row {
// It's important to invoke reloadRowsAtIndexPaths implementation on main thread, as it wont work on non-UI thread
dispatch_async(dispatch_get_main_queue(), ^{
NSUInteger lastSection = [self.tableView numberOfSections];
if (lastSection == 0) {
return;
}
lastSection -= 1;
if (section > lastSection) {
return;
}
NSUInteger lastRowNumber = [self.tableView numberOfRowsInSection:section];
if (lastRowNumber == 0) {
return;
}
lastRowNumber -= 1;
if (row > lastRowNumber) {
return;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
#try {
if ([[self.tableView indexPathsForVisibleRows] indexOfObject:indexPath] == NSNotFound) {
// Cells not visible can be ignored
return;
}
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
#catch ( NSException *e ) {
// Don't really care if it doesn't work.
// It's just to refresh the view and if an exception occurs it's most likely that that is what's happening in parallel.
// Nothing needs done
return;
}
});
}
After many try, I found "reloadRowsAtIndexPaths" can be only used in certain places if only change the cell content not insert or delete cells. Not any place can use it, even you wrap it in
[self beginUpdates];
//reloadRowsAtIndexPaths
[self endUpdates];
The places I found that can use it are:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (IBAction) unwindToMealList: (UIStoryboardSegue *) sender
Any try from other places like call it from "viewDidLoad" or "viewDidAppear", either will not take effect (For the cell already loaded I mean, reload will not take effect) or cause exception.
So try to use "reloadRowsAtIndexPaths" only in those places.
You should check cell visibility before reload. Here is Swift 3 code:
let indexPath = IndexPath(row: offset, section: 0)
let isVisible = tableView.indexPathsForVisibleRows?.contains{$0 == indexPath}
if let v = isVisible, v == true {
tableView.reloadRows(at: [indexPath], with: .automatic)
}
I had the same issue. In my case; it was happening only if another view controller pop/pushed over existing table view controller and then[self.tableView reloadRowsAtIndexPaths] function is called.
reloadRowsAtIndexPaths call was hiding/showing different rows in a table view which is having over 30, visually complex, rows. As i try to fix the issue i found that if i slightly scroll the table view app wasn't crashing. Also it wasn't crashing if i don't hide a cell (by returning 0 as height)
To resolve the issue, i simply changed the "(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath" function and returned at least 0.01 as row height.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
return rowModel.height + 0.01; // Add 0.01 to work around the crash issue.
}
it solved the issue for me.
THIS IS OLD. DO NOT USE.
I just bumped into this issue when I was calling reloadRowsAtIndexPaths... in order to change the cell to an editing cell containing a UITextField. The error told me I was deleting all of the rows in the table. To solve the problem, I removed:
[self.tableView beginUpdates];
NSArray *reloadIndexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:count inSection:section]];
[self.tableView reloadRowsAtIndexPaths:reloadIndexPath withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
and replaced it with
[self.tableView reloadData];
The app crashes because you have made some changes to your tableView. Either you have added or deleted some rows to the tableView. Hence when the view controller asks your model controller class for data, there is a mismatch in the indexPaths. Since the indexPaths have changed after modification.
So either you simply remove the call
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
or replace it with
[self.tableView reloadData];
Calling reloadData checks your number of sections, number of rows in each section and then reloads the whole thing.
If data count changes completely, then use reloadData else, there is three functions to do it.
When data count changes we use insertRows / deleteRows and when data count still the same use reloadRows.
Important! don't forget call beginUpdates and endUpdates between insertRows/deleteRows/reloadRows calls.