In case it's not obvious by the title, what I want should be simple, for the tableView
to start scrolled all the way to the bottom when the users first sees it (before he sees it, and without animations).
So, I know that this has been answered a few times, but none of those solutions seem to work right now. To give some context, I'm using Swift, autolayout and latest version of iOS as of today.
Constraints
There are some things I need to support:
UITableView
is inside a UIScrollView
(UIScrollView
scrolls horizontally and UITableView
scrolls vertically).UITableView
is in a childViewController
, i.e., I add it to the main UIViewController
(which, for some reason, makes viewWillAppear
not get called - viewDidAppear
is still called).I'll make a summary of what I tried:
As for implementations:
tableView.contentOffset = CGPoint(x: 0, y: CGFloat.max
tableView.scrollToRowAtIndexPath(indexPathForLastItem, atScrollPosition: .Bottom, animated: false)
var contentOffset = tableView.contentOffset
contentOffset.y = tableView.contentSize.height - tableView.bounds.size.height
tableView.setContentOffset(contentOffset, animated: false)
dispatch_async(dispatch_get_main_queue(), {
// tried option 1., 2. and 3. here
})
As for places I've tried to call those implementations:
viewDidLoad
viewWillAppear
viewDidLayoutSubviews
(only the first time it's called, I use a property to track that)viewDidAppear
(even though this wouldn't give me what I want)didSet
Before those I always called tableView.reloadData
What I DON'T want to do:
tableView.transform = CGAffineTransformMakeScale(1, -1)
+
cell.transform = CGAffineTransformMakeScale(1, -1)
(I'm assuming that if you think of suggesting this solution, it's because you know the hack I'm talking about. If you don't, then you won't be suggesting this, so you don't need to understand how it works)
One of the reasons why I don't want this one is because now I can't scroll to the top...
Problems I've noticed:
tableView
's contentSize
(as a UIScrollView
subclass) changes when you scroll for the first time after it appeared. Yes, I meant contentSize
, not contentOffset
, and it changes more than once while you scroll. After you've scrolled through the entire tableView
once, it doesn't change anymore.viewWillAppear
doesn't get called, but viewDidAppear
does.Any solutions (except for the one I mentioned I don't want) would be very much appreciated. Even hacky ones, as long as they don't break other stuff.
Just as a side note, scrollToRowAtIndexPath...
does not scale, i.e., it is too slow if you have, say, 2000 items. So Ideally I'd prefer a solution that scales.
EDITS AFTER ANSWERS:
After @bsmith11's answer I tried:
private var called = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !called {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.setContentOffset(CGPoint(x: 0, y: self.tableView.bounds.height), animated: false)
})
called = true
}
}
And it didn't work.
To scroll to the bottom of the table view items, use ScrollToRow. Three parameters are required for this: NSIndexPath: Specify which row item to scroll to. UITableViewScrollPosition: An enumeration of predefined scroll positions.
Smooth Scrolling:- As a result, it affects the smoother scrolling experience. In UITableVIew, Scrolling will be smooth through cell reuse with the help of prepareForReuse() method.
In viewDidLayoutSubviews()
, set the .contentOffset
to tableView.bounds.height
. You will need to kick this off in an async block to give the tableView
time to load it's content.
dispatch_async(dispatch_get_main_queue()) {
self.tableView.setContentOffset(tableView.bounds.height, animated: false)
}
viewDidLayoutSubviews()
can get called multiple times, so you probably want to make sure your code above only gets called once.
Just tried with below code it works fine.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
myArray = [["Data": "1234567890", "Height": 44],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "12345678901234567890", "Height": 88],["Data": "End of Table", "Height": 132]]
let no = myArray.count-1
let lastIndex = NSIndexPath(forRow: no, inSection: 0)
table1.scrollToRowAtIndexPath(lastIndex, atScrollPosition: .Bottom, animated: true)
}
Download sample code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With