Reputation:
I'm having an issue with UITableView
where it doesn't seem to handle scrolling to the last row properly when using scrollToRowAtIndexPath:atScrollPosition:animated
Here's the code I'm using to cause the scroll:
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionMiddle
animated:YES];
And here's a screenshot showing the result (and the issue!):
As you can see, the very last row (September) isn't scrolled fully into view; the bottom few pixels are cut-off.
I've tried using UITableViewScrollPositionNone
, UITableViewScrollPositionTop
and UITableViewScrollPositionBottom
as the scroll position but they all produce the same result.
My cell does have a custom cell height of 61.0f
which is currently set in the storyboard, but adding the UITableViewDelegate
method tableView:heightForRowAtIndexPath:
and returning the same value doesn't help either.
Is there any way I can get the table view to scroll to the last row AND have it fully visible?
EDIT:
Just to be clear, I'm using a stock UINavigationController
with a stock UITableViewController
as it's root view controller.
EDIT 2:
If I use rectForRowAtIndexPath:
to determine the rect for the row, it does in-fact return the correct rect for that row. But if I then call scrollRectToVisible:animated:
using that rect, I get the same result as above; the bottom few pixels are cut-off.
Upvotes: 17
Views: 7721
Reputation: 12011
I've noticed the same bug when I was scrolling to the particular row of the tableView
when user touches back button. I noticed that contentInset
of the table view is changing during the navigation (due to automaticallyAdjustsScrollViewInsets = true
). The only solution I found is to add an observer:
tableView.addObserver(self, forKeyPath: "contentInset", options: .new, context: nil)
and perform the scrollToRow
method after the contentInset
establishes. This is not a beautiful solution, but it works.
Upvotes: 0
Reputation:
Okay, I've successfully been able to fix this issue.
The clue was in this comment by Matt Di Pasquale on a semi-related question.
It turns out that in iOS 7, the views are yet to be laid out when viewWillAppear:
is called, meaning the frame and bounds of the table view are not guaranteed to be accurate. Since the call to scrollToRowAtIndexPath:animated
must use at least one of these to calculate the offset, this makes sense as to why in my case it wasn't being handled properly.
I think in most cases people won't encounter this issue as their presenting view controller will likely have the same bounds as the presented view controller. But in my case, I was presenting a view controller that had a visible navigation bar and status bar from one that didn't have either, ergo there was an extra 64pts to account for. As can be seen in this console output:
2014-03-15 19:14:32.129 Capture[3375:60b] viewWillAppear, tv bounds: {{0, 0}, {320, 568}}
2014-03-15 19:14:32.131 Capture[3375:60b] viewDidLayoutSubviews, tv bounds: {{0, -64}, {320, 568}}
To get around the issue, I now set a flag in viewWillAppear:
that signifies there's a pending scroll and then in viewDidLayoutSubviews
, if the flag is set, I call scrollToRowAtIndexPath:animated
and unset the flag (since this method is called numerous times). This works flawlessly.
Hopefully this will help anyone else coming across the issue.
Upvotes: 28
Reputation: 11112
I would also suggest you turn of a UIViewController
property, which is on by default:
@property (nonatomic, assign) BOOL automaticallyAdjustsScrollViewInsets;
This one has helped me with many issues, when I did not know why UITableView
(which is a subclass of UIScrollView
actually) was not scrolling properly. Depending on your layout of course.
This property is available since iOS 7, read more about it here:
http://b2cloud.com.au/general-thoughts/uiviewcontroller-changes-in-ios7
Upvotes: -1
Reputation: 25144
What about using a lower-level function? After all, a UITableView
is a UIScrollView
.
[self.tableView setContentOffset:(CGPoint){0, self.tableView.contentSize.height - self.tableView.bounds.size.height} animated:YES];
(Adjust the offset as desired if you have content edge insets other than 0).
Upvotes: 2