I have a UITableview with section and index title. When user click on a cell a option view of height 42 gets display in that cell. To display that option I reload the cell. But section header is also getting updated. That gives bizarre animation. How to reload only cell,without getting called for other delegate method like heightForHeaderInSection and viewForHeaderInSection.
I think you should only update the particular row for indexPath, try the code below
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}
It might help you.
Cheers.
In Swift 2.0 & above
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
tableView.endUpdates()
}
There are several methods for reloading the tableview.
To reload whole section including section's cell.
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
Example:
[tableVIewContent reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
To reload single/multiple cells
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath > )indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Example:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationFade];
To reload whole tableview.
- (void)reloadData;
// reloads everything from scratch. redisplays visible rows. because we only keep info about visible rows, this is cheap. will adjust offset if table shrinks
Related
In didSelectRowAtIndexPath method of my Table View Controller for some reasons i should call reloadData of my Table View. After this any selection will disappear. Which is proper way to preserve selection of table?
UPD: i tried to do selectRowAtIndexPath after i reload table in didSelectRowAtIndexPath, but it didn't lead to any result, selection is still missing. But it will work if i will do it in dispatch_after block, although it's a very strange solution. I need to do it in a right way.
Add -selectRowAtIndexPath with animated flag set to NO after you call [tableView reloadData] inside -didSelectRowAtIndexPath.
Objective-C:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
...
[tableView reloadData];
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
...
}
Swift:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
...
tableView.reloadData()
tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
...
}
You can use.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//add you code and reload the table
}
The reload happens during the next layout pass, which normally happens when you return control to the run loop (after, say, your button action or whatever returns).
So one way to run something after the table view reloads is simply to force the table view to perform layout immediately:
- (void)reloadTableView
{
NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
for (NSIndexPath *path in indexPaths) {
[self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Or as you said you can use GCD to schedule your after-layout code to run later
- (void)reloadTableView
{
NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
for (NSIndexPath *path in indexPaths) {
[self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
}
In "didSelectRowAtIndexPath" delegate method, keep a reference of your selected indexPath:
indexPathReference = indexPath;
And after reloading the table view add this line:
[tableView selectRowAtIndexPath:indexPathReference animated:NO scrollPosition:UITableViewScrollPositionNone];
In didSelectRowAtIndexPath you must keep a reference to the selected indexPath.
Then you should call reloadData
reloadData will trigger calls to cellForRowAtIndexPath:
That's where you check each index with your stored indexPath and set the cell as selected. (cell.selected = true)
I have a UITableView which I have hooked up to a NSFetchedResultsController. When an object is updated, I use reloadRowsAtIndexPaths to reload the cell. I want to keep the cell selected while it is reloaded. The below code is how I am reselecting the cell after the update. It works, but the cell displays unselected for about half a second before becoming selected again and looks bad. self.selectedIndexPath is set in didSelectRowAtIndexPath.
I've also tried setting the cell to selected and highlighted in cellForRowAtIndexPath, but that did nothing.
Does anyone know how to prevent the flickering? I'm using iOS 8.3 BTW.
// Called in controllerWillChangeContent
[self.tableView beginUpdates];
...
// Called in didChangeObject
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
...
// Called in controllerDidChangeContent
[self.tableView endUpdates];
if( self.selectedIndexPath ) {
[self.tableView selectRowAtIndexPath:self.selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
I have a table view with two rows in one section, i.e
---
Row A
Row B
If I want to animate row 1 into a new section, i.e. the end result being:
---
Row B
---
Row A
how should this be achieved? What I have tried already is:
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow: 0 inSection: 0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
However, in iOS 8 at least, the row animates out but then the new section is un-rendered (i.e. white, no bottom row border) until some time after when something triggers a redraw and it comes back.
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Which raises an exception:
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).
And:
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Which raises the exception:
cannot move a row into a newly inserted section (1)
Should I just not animate at all and reload the whole table?
You'll need to perform this in two steps.
First you'll need to add the section; you need to tell your data source to account for the new section. For a simple example like yours, you can just set a variable:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if(hasTwoSections)
return 2;
return 1;
}
Then where you want to trigger the animation, call:
hasTwoSections = true; // this needs to be set before endUpdates
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
At this point, the tableView will call the data source methods to update the table based on your changes.
Once the section has been added, you can move the row to it in another update block:
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Obviously you'll need to update what numberOfRowsInSection returns to account for the change.
Although it's two steps, it will look like it's happening all at once.
I was in a similar situation where I was getting the exception cannot move a row into a newly inserted section (1). I only found two half solutions.
Reload the table with reloadData() - aggressive, and no animations
Delete and insert your rows, code below:
self.requestTableView.deleteRowsAtIndexPaths([oldIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
self.requestTableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
To get animations effects that give the impression of the rows moving, you could use UITableViewRowAnimation.Top when deleting, inserting, or reloading sections.
self.tableView.deleteSections(toDelete, withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.insertSections(toInsert, withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.reloadSections(toReload, withRowAnimation: UITableViewRowAnimation.Top)
Use -[UITableView moveRowAtIndexPath:toIndexPath:]
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];
I change the height of a cell (when image downloaded) with method like that
-(void)didHeightChanged:(float)imageHeight atIndex:(NSInteger)indexPath
{
[dictionary setObject:imageHeight forKey:indexPath];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
Everything works well with first data, but when I reload UITableView with second data , probably beginUpdates method doesnt ended and I got issues - when I delete and reload UITableView, cell dont disappear, UITableView shows wrong cells
How can I stop beginUpdates immediately before reload new data?
You should either call [self.tableView reloadData] or [self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathWithRow:index inSection:0]]]; // or indexPath if instead of passing a NSInteger you pass a NSIndexPath * to the method.
I found the answer
[self.tableView beginUpdates];
[self.tableView endUpdates];
it was called in background thread...