views:

339

answers:

3

In Apple's Messages app, when you click a correspondent's name and switch to the table view of the conversation (with balloons for each message), the table appears scrolled all the way to the end. No animation or anything, it's just there.

Similarly, in Tweetie 2, when you load the tweets view, it appears right where you last looked at it. No animation to get there, it's just there, as if none of the cells above were loaded.

How do these apps do this? Are they calling scrollToRowAtIndexPath:atScrollPosition:animated: somewhere in the table controller? If so, how do they know what to pass to atScrollPosition:? And in what method is it called?

A: 

You can call -scrollToRowAtIndexPath:atScrollPosition:animated within the -viewWillAppear: method of your TableViewController.

atScrollPosition: allows you to set where you want your cell for rowAtIndexPath to appear. There are four options:

UITableViewScrollPositionTop - puts your cell right at the top of the view

UITableViewScrollPositionMiddle - centers your cell in the view

UITableViewScrollPositionBottom - puts your cell at the bottom

UITableViewScrollPositionNone - Using this setting will position in the cell in user view with minimum scrolling/movement.

The behavior is different in three scenarios :-

If the cell is already in view, it does nothing.

If the cell is above the current view, it scrolls the cell to the top of the view.

If the cell is beneath the current view, it scrolls the cell to the bottom of the view.

Deepak
+1  A: 

scrollToRowAtIndexPath should work.

In viewWillAppear, try this:

[theTableView reloadData];    
NSIndexPath* ip = [NSIndexPath indexPathForRow:rowNumberHere inSection:sectionNumberHere];
[theTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];

rowNumberHere is the row# in the data source you want to scroll to.

atScrollPosition is just one of the values in the UITableViewScrollPosition enum which can determine where on the screen the row# you want will show up. However, depending on the number of rows and which row you are scrolling to, it may not make a difference.

Putting reloadData avoids an exception if the data is not loaded yet in viewWillAppear. If you put the scrollToRowAtIndexPath in viewDidAppear, you would not need the reloadData but you will see the table jump a little which you say you don't want.

Edit: @Theory, try changing your code as follows...

[tableView reloadData];
int lastRowNumber = [tableView numberOfRowsInSection:0] - 1;
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
[tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];

Please note numberOfRowsInSection returns row count, not the last row# (which is row count - 1).

DyingCactus
Thanks, this appears to be exactly what I needed to know, except that my implementation crashes the app. :-( See my "answer" below.
Theory
D’oh! Of course, I missed that I wanted the cell index number not the row number. Thanks! I'll update my own answer to show my specific solution.
Theory
A: 

Following DyingCactus's reply above, I added this method to my controller:

-(void)viewWillAppear:(BOOL)animated {
      [self.tableView reloadData];    
      NSIndexPath* ip = [NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:0] - 1 inSection:0];
      [self.tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

And now it works, exactly what I wanted. Thanks!

Theory
The line where you set the NSIndexPath doesn't look right. I will edit my answer with what I think it should be.
DyingCactus
Edited to reflect the fix. Silly of me not to have realized that I was passing `mumberOfRowsInSection` rather than the index. Thanks again!
Theory
Great. You may also want to add in a check for zero rows and not call the scroll in that case unless your tableview will always have at least one row.
DyingCactus
I thought exactly the same thing this morning in the shower. Will do.
Theory