I am scrolling multiple horizontal collectionViews to the last (Far right) item using:
timelineCollectionView.scrollToMaxContentOffset(animated: false)
postsCollectionView.scrollToMaxContentOffset(animated: false)
this works great, except I can't figure out where to put them.
Various places I've tried:
viewWillAppear - it doesn't scroll as though the cells aren't fully loaded yet.
viewDidAppear - it does scroll perfectly. Awesome! Except now you can see it scroll even when you put animated: false. The collectionView loads at the far left for a split second before updating to the far right
viewDidLayoutSubviews - this works perfectly - timing and everything! However, it gets called many times. If I could determine which subview was just laid out, perhaps I could scroll it in there but Im not sure how.
Is there a better option? How can I do this?
Also these are the functions I am using to set content offset:
extension UIScrollView {
var minContentOffset: CGPoint {
return CGPoint(
x: -contentInset.left,
y: -contentInset.top)
}
var maxContentOffset: CGPoint {
return CGPoint(
x: contentSize.width - bounds.width + contentInset.right,
y: contentSize.height - bounds.height + contentInset.bottom)
}
func scrollToMinContentOffset(animated: Bool) {
setContentOffset(minContentOffset, animated: animated)
}
func scrollToMaxContentOffset(animated: Bool) {
setContentOffset(maxContentOffset, animated: animated)
}
}
Is there a better option?
Yes there is, and that would be UICollectionView.scrollToItem(at:at:animated:)
Next is to wait until cells are dequeued so we can call UICollectionView.scrollToItem(at:at:animated:)
, and there are multiple ways you can do this.
You can call UICollectionView.reloadData
within animateWithDuration:animations:completion
it works both in viewDidLoad
or viewWillAppear
, scrolling is seemless too, like so:
func scrollToItem(at index: Int) {
UIView.animate(withDuration: 0.0, animations: { [weak self] in
self?.collectionView.reloadData()
}, completion: { [weak self] (finished) in
if let count = self?.dataSource?.count, index < count {
let indexPath = IndexPath(item: index, section: 0)
self!.collectionView.scrollToItem(at: indexPath, at: UICollectionView.ScrollPosition.right, animated: false)
}
})
}
Another way is to dispatch a code block to main queue after UICollectionView.reloadData
, like so:
func scrollToItem(at index: Int) {
self.collectionView.reloadData()
DispatchQueue.main.async { [weak self] in
// this will be executed after cells dequeuing
if let count = self?.dataSource?.count, index < count {
let indexPath = IndexPath(item: index, section: 0)
self!.collectionView.scrollToItem(at: indexPath, at: UICollectionView.ScrollPosition.right, animated: false)
}
}
}
I have also created a gist where I am using a horizontal UICollectionView
, just create a new xcode project and replace ViewController.swift with the gist source 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