tableView reloadData and deselectRowAtIndexPath - ios

I am using the following code to deselect a selected table view cell when returning back to the table view in -viewWillAppear:animated.
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
I also need to reload the table view's data in this case but when you do that it clears the selected state of the selected cell so you don't see any fade animation.
Is there a way to reload the table data and also preserve the selected state to create the deselect animation?

After several attempts, I've found something that works. You need to set the deselection to occur after a "delay" (of 0 seconds) in order to make sure it happens on the next draw cycle and gets animated properly.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self performSelector:#selector(deselectRow) withObject:nil afterDelay:0];
}
- (void)deselectRow
{
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}

Try this in your viewDidLoad:
[self setClearsSelectionOnViewWillAppear:NO];

The obvious solution suggested by #user2970476 seems to work fine on iOS 7. For iOS 8 I slightly modified #Stonz2's answer to use blocks
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^(void) { // necessary for iOS 8
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
});
}
I also had to set self.clearsSelectionOnViewWillAppear = NO; in viewDidLoad because the IB setting got ignored.

You will need to reload your table view first, select the row you want to indicate then perform the deselect animation. The issue you are having is your order of operation is incorrect.

You can save the current selection with
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedRow animated:NO scrollPosition:UITableViewScrollPositionNone];

Related

request for index path for global index 1793701674059 when there are only 6 items in the collection view

UICollectionView crash when performBatchUpdates after reload data.I don't perform any delete or add operation,only refresh data after net request success.
code like this
- (void)reloadData:(NSArray *)dataArray {
self.dataSourceArray = dataArray;
[_collectionView reloadData];
[_collectionView performBatchUpdates:nil completion:^(BOOL finished) {
if ([_collectionView numberOfItemsInSection:0] > 0) {
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
}
}];
}
function reloadData maybe call twice consecutive and fast,everytime use new data reload collection view.
i think somewhere use incorrect with collection view,but i can't find it.

UITableView Flickering with UIRefreshControl

I have aUITableView withUIRefreshControl defined inside the viewDidLoad: method of aUIViewController as below:
- (void)viewDidLoad {
[super viewDidLoad];
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refreshMessages) forControlEvents:UIControlEventValueChanged];
UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.messagesTableView;
tableViewController.refreshControl = self.refreshControl;
}
- (void)refreshMessages {
//load data items
if ([self.refreshControl isRefreshing]) {
[self.refreshControl endRefreshing];
}
[self.messagesTableView reloadData];
}
I use self sizing cell to update the cell data. On re-loading the data theUITableView flickers. Is there a way to avoid this?
Edit
I have linked the video file for a sample demo. The UITableView on reload data, flickers. I use autolayout to update the dynamic cell heights.
Here is a sample code that can recreate this issue. The UITableView should be pulled to refresh. The table view is reloaded on the main queue. The cell are being resized with autolayout.
I got the same issue today and I managed to solve the problem using CATransaction, here is a code snippet in Swift, executed within a UITableViewController :
CATransaction.begin()
CATransaction.setCompletionBlock { () -> Void in
/*wait for endRefreshing animation to complete
before reloadData so table view does not flicker to top
then continue endRefreshing animation */
self.tableView.reloadData()
}
self.refreshControl!.endRefreshing()
CATransaction.commit()
The problem is that you are calling -reloadData and -endRefreshing together. Both want to update the view. You need to separate them a bit.
One of these options will work, depending on where you want to see the cells update:
[self.tableView reloadData];
[self.refreshControl performSelector:#selector(endRefreshing) withObject:nil afterDelay:0.5];
[self.refreshControl endRefreshing];
[self.tableView performSelector:#selector(reloadData) withObject:nil afterDelay:0.5];
Probably you need less delay with the first option. Even 0.0 seconds seemed to work in your example code. That's enough to get the -endRefreshing animation on the next screen refresh.
Swift
You may want to use
extendedLayoutIncludesOpaqueBars = true
in your ViewController in order to have correct UIRefreshControl animation.
I guess the issue is using estimatedHeight & UITableViewAutomaticDimension along with uitableview's reloadData. This has been reported here
The delay would work but its still not the right way to achieve it unless you want a hackish work around.
May be calling reloadData and endRefreshing could have caused the flicker to take place so what i did was modified your code a bit and removed the dispatch block which cleared off the flick
- (void)refreshMessages{
self.messages = [NSMutableArray new];
[self loadData];
[self.tableView reloadData];
[self.refreshControl performSelector:#selector(endRefreshing) withObject:nil afterDelay:0.5];
// double delayInSeconds = 0.5;
// dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
// dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// [self.refreshControl endRefreshing];
// });
}
Have you tried?
[self.messagesTableView beginUpdates];
[self.messagesTableView reloadSections:[NSIndexSet indexSetWithIndex:0/*target section*/] withRowAnimation:UITableViewRowAnimationNone];
[self.messagesTableView endUpdates];
instead of:
[self.messagesTableView reloadData];
-(void)refreshMessages{
self.messages = [NSMutableArray new];
[self loadData];
[self.tableView reloadData];
[self.refreshControl endRefreshing];
}

Reloading Table and Clearing Cell Row Selection

Up to iOS 8 and Xcode 6, I was able to use a UITableViewController much like Apple's Settings to configure my applications. In segue'ing from one table back to the calling table, I would reload the calling table to update data in the cells, re-select the calling row and then let the view appear animated to clear the selection.
Here's the code in the viewWillAppear method I've been using:
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:lastselected animated:YES scrollPosition:UITableViewScrollPositionNone];
[super viewWillAppear:animated];
While the cell data is updated, the nice slow disappearance of the selected row/cell doesn't happen. It is effectively cleared immediately. I've tried just about everything I can think of and can't get this working. Please help...
What has changed and how can I get that nice transition back?
Thank you.
viewWillAppear is called before your view is on the screen. So you'll not get any animations working correctly if fired from inside that method. Use viewDidAppear instead and you should see more reliable animations on transition.. Potentially use performSelector with a delay as well if you feel it's happening too quickly.
Personally as well, I'd call [super viewDidAppear:animated]; first in this case. I can't see a reason why it'd be below the other lines of code.
Also just so I'm being 100% clear... viewWillAppear is obviously ok for reloading the tableView so lets do this...
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView selectRowAtIndexPath:lastSelected animated:YES scrollPosition:UITableViewScrollPositionNone];
}
Or with the perform selector delay on viewDidAppear...
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(animateCellSelectionAtIndexPath:) withObject:lastSelected afterDelay:0.2f];
}
-(void)animateCellSelectionAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
}

Select the newly added dynamic row in UITableView

I am working with a MasterDetail View using SplitViewController in iPad, My Master View has a tableView where I add rows dynamically. So the newly added row sits at zero postion. So what I want to achieve is that, I want to select the newly added row as soon as it is added to the Master table. Later when the user adds a new row, that row should be selected and the previously added row should be deselected.
For this I have written the code below
- (void)selectNewlyAddedRow
{
NSIndexPath* selectedCellIndexPath= [NSIndexPath indexPathForRow:0 inSection:0];
[m_tableView selectRowAtIndexPath:selectedCellIndexPath animated:TRUE scrollPosition:UITableViewScrollPositionNone];
[self tableView:m_tableView didSelectRowAtIndexPath:selectedCellIndexPath];
}
This code works if I write it in function ViewDidAppear, but if I write it in cellForRowAtIndexPath it doesnt work..
Please provide me correct understanding where I am going wrong.
Regards
Ranjit
viewDidAppear wont get call when you add new row so you just register for table reloaded notification and in method for that notification call select first row I hope it will help you....
- (void)selectFirstRow{
if ([self.tableView numberOfSections] > 0 && [self.tableView numberOfRowsInSection:0] > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
}
Implement this delegate -
-(void)tableView:(UITableView )tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath)indexPath{
[NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:#"Table Reloaded" object:nil];
}
In your view controller add this line in viewdidload
-(void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tableReloaded:) name:#"Table Reloaded" object:nil];
}
-(void) tableReloaded:(NSNotification*)notification{
[self selectFirstRow];
}

How to set selected first cell by default in SASlideMenu?

I am using this script for slide menu "SASlideMenu" and I am trying to select the first item by default, but with no success. Does anybody know how to do it?
I tried this:
(void) viewDidLoad{
[super viewDidLoad];
...
self.tableView.delegate = self;
NSString *CellIdentifier = #"homeCell";
UITableViewCell *homeCell = [self.tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
[homeCell setSelected:YES];
}
UPDATE (improved explanation)
I am doing this in "SASlideMenuViewController.m"
UPDATE 2
Further to main question I would like to ask when user enters applicationWillEnterForeground how to select then first cell and switch to home page?
P.S. I dont want to open another question for that, because it could be treated like a "duplicate".
UPDATE 3
I don't know how to switch to home page when user is on another view. For example we switch user to home page when he came back to app(foreground), but this doesn't work in next situation; in SASlideMenu I have list of categories and when I go to one of this categories and then exit from app and back again into app, it switch me to home page and that's ok. But when I click on one of categories in SASlideMenu and then click inside of category view on one of articles and exit app and enter again then it does not switch me to home page and I don't know why. It's navigation based app.
Question 1: To select the first item by default on launch of app
Assuming that you are using the SASlideMenu code mentioned in the question. In your ExampleMenuViewController class you need to add the following,
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
if (indexPath) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
__________________________________________________________________________________
Question 2: To select the first item by default and to set home screen in applicationWillEnterForeground
Add the following method declaration in your ExampleMenuViewController.h file,
#interface ExampleMenuViewController :SASlideMenuViewController<SASlideMenuDataSource>
- (void)selectFirstRowAndSetHomePage;
#end
Add the following method in your ExampleMenuViewController.m file,
- (void)selectFirstRowAndSetHomePage {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
if (indexPath) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
NSString *identifier= [self.slideMenuDataSource initialSegueId];
if (identifier) {
[self performSegueWithIdentifier:identifier sender:self];
}
}
Implement the following in your SASlideMenuAppDelegate.h class,
#import "ExampleMenuViewController.h"
And,
- (void)applicationWillEnterForeground:(UIApplication *)application {
ExampleMenuViewController *exampleMenuViewController = (ExampleMenuViewController *)self.window.rootViewController;
[exampleMenuViewController selectFirstRowAndSetHomePage];
}
That should help.
__________________________________________________________________________________
This should work for you:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
Check with this code:
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:0];
UITableViewCell *homeCell = [[self.tableView cellForRowAtIndexPath:indexPath] setSelected:YES];
When viewDidLoad is called your table is still empty. You are not even taking a cell which is inside the table.
Try this code:
NSIndexPath firstCellIndex = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:firstCellIndex
animated:NO
scrollPosition:UITableViewScrollPositionNone];
And put it into viewDidAppear.

Resources