Update Specific cells in a tableView without calling reloadData - ios

I have an app in which I have a UITableview with custom cells and headers. The cells have an inputView so when selected they become first responder and allow the user to input data.
I want to be able to update the visible TableViewCell and header information on the fly while the user is changing it.. easy, just call [tableview reloadData]; ..
Unfortunately this causes the inputview to resign first responder and hide itself.
Is there any way that I can get a reference to the cell itself inside the UITableview so that I can just change the text property? (cellForRow:atIndexPath: returns a new object with the same properties so doesn't work) It seems like the only easy solution may be to store a reference the cells in a dictionary each time a cell is populated, not really the ideal solution.
cellForRowAtIndexPath is literally just
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *orderCell;
static NSString *productCellIdentifier = #"ImageDetailCellIdentifier";
orderCell = [self.tableView dequeueReusableCellWithIdentifier:productCellIdentifier forIndexPath:indexPath];
//set a bunch of properties orderCell.blah
return orderCell;
}

According to UITableView documentation, -cellForRowAtIndexPath: returns an object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
That is also how I remember it. I don't think your observation is correct that it returns a new object. If the cell is visible you will get hold of it.

[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade]; ///as your choice in animation
[tableView endUpdates];
or else
[tableView beginUpdates];
// do some work what ever u need
[tableView endUpdates];

For reloading specific rows, you can use
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
For example,
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:2 inSection:1];
NSArray* indexArray = [NSArray arrayWithObject:indexPath];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

Related

In Objective C UITableVIew how to retain scroll position after calling reload

In Objective C UITableVIew how to retain scroll position after calling reload
While selecting a cell I call reload function of the UITableView but after reload I want the scroll to maintain its position (i.e, Position where the cell was tapped)
You can use reloadRowsAtIndexPaths:withRowAnimation: instead of reloadData.
You could do something like this.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
Also if you want to scroll to some particular position, you can use this.
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
Two lines and you get the desired result:
NSArray *indexPaths = tableView.indexPathsForVisibleRows;
[tableView reloadRowsAtIndexPaths:indexPathswithRowAnimation:UITableViewRowAnimationNone];'

Validity of the UITableView Cells in ViewWill Appear

What I have
1). Container has UITableView, which has two custom UITableViewCells.
2). Core Data has certain entity which has a text to be displayed at
UITableViewCell each time I get into the View.
What i am doing ?
1) I have chosen -viewWillAppear method which gets invoked each time the view is visible.
2) In -viewWillAppear, I retrieved the data from core data.
3) Retrieved particular cell from UITableView
NSUInteger idxArr[] ={2,0}; // 2 nd section, 0th Row.
NSIndexPath *cPath = [NSIndexPath indexPathWithIndexes:idxArr length:2];
myCell *tCell = (myCell *)[self.settings cellForRowAtIndexPath:cPath];
tCell.myLabel.text = rec.servername; // rec.servername is from DC.
When I checked in the lldb,
tCell was nil.
Questions:
1) It is the right way of getting the Cell ?
2) Or, By the time -viewWillAppear, does the UITableView not Ready ?
I am sure.
You should populate the cells by conforming to tableView dataSource protocol and then in your viewWillAppear you should call reloadData on your tableView.
After calling reloadData for tableview, We need to call -scrollToRowAtIndexPath: before getting cell from -cellForRowAtIndexPath:.
Because, As we are calling a row in section 2, it might not be in the visible area until we scroll. So, cellForRowAtIndexPath: returns nil.
Method -cellForRowAtIndexPath: shouldn't be called programically. It's a data source method for UITableView and it contain some cell reuse optimalizations. If you update the view after scrolling down and up -tableView:cellForRowAtIndexPath will be called again and your changes won't be visible.
If you want to update specific cell you should update make changes in:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YourCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellId" forIndexPath:indexPath];
YourData *data = //Get your data here
if (data.isReady) {
cell.tf.text = data[indexPath.row].text;
} else {
cell.tf.text = #"Not ready yet. Need to reload this cell later";
}
return cell;
}
And then call method below when you finish fetch your data.
[self.tableView reloadRowsAtIndexPaths:(NSArray *) withRowAnimation:UITableViewRowAnimationFade];
If you want to reload whole tableView (usually it's not slow) as #salaman140 says you can call [self.tableView reloadData] to update all visible cells.
If I were you I wouldn't use:
NSUInteger idxArr[] ={2,0}; // 2 nd section, 0th Row.
NSIndexPath *cPath = [NSIndexPath indexPathWithIndexes:idxArr length:2];
I would (is much more clear):
NSIndexPath *cPath = [NSIndexPath indexPathForRow:0 inSection:2];

Tableview - wait for cell to be removed before adding a new one

I have a table view, in which if you select a row, it add a picker below the row. But if another row is selected, it hides the first picker (removed the row with the picker) and then adds a new picker.
Now my question is, how to I wait for the remove animation to finish before adding a new row?
This is how I add a new row. It's in the didSelectRow method.
NSIndexPath *selectedIndex = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
pickerInsertedAtIndexPath = selectedIndex;
NSArray *indexes = #[selectedIndex];
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationMiddle];
[tableView endUpdates];
This is how I remove a row. This is in the willSelectRow method.
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationMiddle];
[tableView endUpdates];
Unfortunately there are no callbacks as to when the table view animations end. You'll have to wait for an interval of your choice before adding the new picker.
Not sure if this matches your scenario but can you use:
tableView:didEndDisplayingCell:forRowAtIndexPath:
A UITableViewDelegate callback that will inform you when a cell has been removed from the UITableView?

how can i acess a cells object if selected

i seem to be having some difficulty trying to access a particular cell, i just need to change one of its objects when i select it in the tableview. There must be a way more efficient way than just calling reloadData all over again. Because thats the only way i could make a table cell look different, by rebuilding it when i select on one. Thanks for all the help :)
[self updatePreferences];
[tableView reloadData];
This is all i have atm on the method where it handles the interaction with table cells.
Use the following code.To reload a single cell.
NSArray *array = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:row inSection:section]];
[self.tableView reloadRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationNone];
You could also make the cell a custom one and add a method to it like update or refresh.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell refresh];
}

Force UITableView to call cellForRowAtIndexPath: for all cells

I am having some trouble with UITableView's reloadData method. I have found that it only calls cellForRowAtIndexPath if there are new cells being added or taken away.
Example: I have five cells, each containing five strings. If I add a new cell and call reloadData, the table is updated and I see it. Yet if I go into one of the five cells, add a new string, then return and call reloadData, none of the table view's delegate methods is called.
My question: Is it possible to force the table view to completely reload the data in all of its visible cells?
I found the problem- I had my cell customization code in the if(cell == nil) block, so because the cells were being recycled, they weren't being changed. Taking my customization code out of that block fixed the problem.
I've found that reloading sections reloads data more readily. If you just have one section you can try:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
You can try this
[tableView reloadData];
or
[tableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
put this in commitEditingStyle method.
Well, the table view's only going to call cellForRowAtIndexPath on the visible cells but if you do call reloadData it will do that so there's something else going on here and I'm not sure what it is.
Even if your tableview has 50 rows, there only will exist as much cells as can be visible at one time. That's the whole story behind the reuseIdentifier. So forcing 'all the cells' doesn't exist. If a new cell appears, the data is loaded dynamically.
The only way to change a cell is to change the data that is delivered by the dataSource method cellForRowAtIndexPath.
Do not take your code out of the if (cell == nil) block. Instead, create a representative identifier for the cell you're making; try and make sure that all of the cell's content is referred to in the identifier. For example, if you have 3 numbers showing, make sure to have those three numbers in the identifier in a unique way that would only refer to a cell that has such content.
Let's say you have three NSArray properties in your class, array1, array2, and array3 that have int values wrapped inside of NSNumber objects. You want to use those NSArrays to fill a UITableView, this is what I'd do:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [NSString stringWithFormat:#"%#-%#-%#",
[[array1 objectAtIndex:indexPath.row] intValue],
[[array2 objectAtIndex:indexPath.row] intValue],
[[array3 objectAtIndex:indexPath.row] intValue]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
//Build your cell here.
}
return cell;
}

Resources