Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where can I set contentOffset to the last item in UICollectionView before the view appears?

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)
    }
}
like image 590
BigBoy1337 Avatar asked Oct 17 '22 08:10

BigBoy1337


1 Answers

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.

like image 92
AamirR Avatar answered Oct 21 '22 03:10

AamirR