34

I have a bit of a problem with my iOS app in xcode. I have a UITableView that loads a few hundred cells. When I scroll down to a specific cell and drill down to detailedviewcontrollers and return again the master view table has returned all the way to the top. I've had a look at the following 2 questions that are similar.

How can I get the UITableView scroll position so I can save it?

Setting scroll position in UITableView

I still can't get these to work. I'm not the most experienced coder so I'm really struggling with this.

I know things like viewWillDisappear and viewDidAppear need to be changed, but I just really can't get much further than that.

On this table I have a reloadData feature so it is possible to pull down the latest data from the server and also a working search bar.

Anyway, a helping hand would be great. Thanks,

Luke

Community
  • 1
  • 1
mylogon
  • 2,772
  • 2
  • 28
  • 42
  • 2
    That's not the default behavior. Seems like somewhere in your code you are scrolling to the top. If you delete that line, you should be all set. Search for `scrollToRowAtIndexPath` maybe. – Desdenova Sep 17 '13 at 07:21
  • 1
    your table is scrolling back to the top when it comes back into view most likely because you're doing a "`reloadData`" call on it in that view controller's "`viewWillAppear:`" method. – Michael Dautermann Sep 17 '13 at 07:21

8 Answers8

70

See here you have to first save the scrolled position of tableView i.e. contentOffset of tableView and then reuse it when you are coming back to tableView.

1)When and where you will save it :

When : At the point, when you are drilling down to detailViewController save the contentOffset of tableView

How : float verticalContentOffset = tableView.contentOffset.y;

2) How will you set tableView back to the same scrolled position :

[tableView setContentOffset:CGPointMake(0, verticalContentOffset)];

Hope this will help you.

Prashant Nikam
  • 2,253
  • 4
  • 17
  • 29
  • Any idea on how to achieve this for a messaging app? As in scrolling up loads more rows but current Y offset is always 0. Meaning that setting the contentOffset to Y = 0 will just scroll that table to the top again – nhenrique Aug 13 '15 at 09:36
13

Sorted!

With a bit of inspiration from Desdenova, whilst at work I had a good think about it and realised what it could be. Remembering that I had a search bar, I had implemented the following code a few months ago to hide it:

[self.tableView setContentOffset:CGPointMake(0,-20) animated:NO];

In naivety I put that in viewDidAppear rather than viewDidLoad. Obviously, this did hide the search bar, but with it being in the wrong void command it did it every time the masterDetailView returned to the top of the stack. So, after moving the above code to viewDidLoad it now still hides it, but only the once.

I'm just spelling it out like this for other beginners, like myself, who may come across the same problem and may just save their sanity!

Thanks to you all for your ideas that helped me out.

+1 to you all!

mylogon
  • 2,772
  • 2
  • 28
  • 42
1

If anyone is wondering, I tried the solution by Prashant N and it worked. However reloadData don't actually reset the scroll position anymore now, so I didn't have to do anything other than reloadData.

Enrico Susatyo
  • 19,372
  • 18
  • 95
  • 156
1

My solution to this problem consisted in scrolling the table to the indexrow of the last item.

private func scrollToItem(item:Int, section:Int bottom:Bool = true, animated:Bool = false) {
    let indexPath = NSIndexPath(forRow: item:Int-1, inSection: section)
    var position = UITableViewScrollPosition.Bottom
    if (!bottom) { position = UITableViewScrollPosition.Top }
    self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: position, animated: animated)
}
1

For some reason this was halfway working for me (sometimes the position would only be approximate). I finally used the following a priori less clean solution

NSIndexPath *firstVisibleIndexPath = [[self.tableView indexPathsForVisibleRows] objectAtIndex:0];
[self.tableView scrollToRowAtIndexPath:self.firstVisibleIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
self.firstVisibleIndexPath = firstVisibleIndexPath;

which worked better.

vib
  • 2,254
  • 2
  • 18
  • 36
  • A fairly long winded route approach, but should work well. Why are you storing the index path as an instance variable? I assume this is just something specific to your project? – mylogon Jan 25 '17 at 10:52
  • Well I thought I was answering something slightly different, this is for a use case with a segmented control and 2 segments. So when you switch segment you save the indexPath for the next time you will come to this segment (observe that the property is set after the scroll is done) – vib Jan 25 '17 at 11:58
  • Does this update the `firstVisibleIndexPath` when you stay on the screen and keep playing with tableview by scrolling it up and down? In which event or delegate method do you update `firstVisibleIndexPath` continuously when the table is scrolled? – Hemant Bavle Jan 24 '19 at 09:44
0

As mentioned in other comments, scrolling to the top on Back navigation in a UINavigationController is not the default behavior.

Another cause might be that you are setting a cell near the top of the table as first responder in viewWillAppear, e.g. a search box or edit field near the top of your table. UITableViewController will scroll a first responder into view automatically, which is nice. As long as that's what you're trying to do. :) Take care to not set first responder in viewWillAppear unless that's what you want. Cheers, AZ

andyzei
  • 31
  • 6
0

My decision for a collection:

CGFloat oldCollectionViewHeight = self.messageCollectionView.contentSize.height;

[self.messageCollectionView reloadData];
[self.messageCollectionView layoutIfNeeded];

CGFloat newCollectionViewHeight = self.messageCollectionView.contentSize.height;

if (oldCollectionViewHeight) {
    self.messageCollectionView.contentOffset = CGPointMake(0, newCollectionViewHeight - oldCollectionViewHeight);
}

For the table, the principle is the same.

Leonardo Alves Machado
  • 2,747
  • 10
  • 38
  • 53
Dmitry Malysh
  • 11
  • 1
  • 4
0

You don’t need to do anything, while you are in the same view controller, your position will be the same. I’m afraid that somewhere in your view controller is being called a reloadData. Search for some method calling scrollToRowAtIndexPath.

aph340
  • 109
  • 4