Paging UICollectionView by cells, not screen

前端 未结 22 2168
予麋鹿
予麋鹿 2020-12-04 05:00

I have UICollectionView with horizontal scrolling and there are always 2 cells side-by-side per the entire screen. I need the scrolling to stop at the begining

22条回答
  •  孤街浪徒
    2020-12-04 05:53

    Here's my implementation in Swift 5 for vertical cell-based paging:

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
    
        guard let collectionView = self.collectionView else {
            let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
            return latestOffset
        }
    
        // Page height used for estimating and calculating paging.
        let pageHeight = self.itemSize.height + self.minimumLineSpacing
    
        // Make an estimation of the current page position.
        let approximatePage = collectionView.contentOffset.y/pageHeight
    
        // Determine the current page based on velocity.
        let currentPage = velocity.y == 0 ? round(approximatePage) : (velocity.y < 0.0 ? floor(approximatePage) : ceil(approximatePage))
    
        // Create custom flickVelocity.
        let flickVelocity = velocity.y * 0.3
    
        // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
        let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)
    
        let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - collectionView.contentInset.top
    
        return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset)
    }
    

    Some notes:

    • Doesn't glitch
    • SET PAGING TO FALSE! (otherwise this won't work)
    • Allows you to set your own flickvelocity easily.
    • If something is still not working after trying this, check if your itemSize actually matches the size of the item as that's often a problem, especially when using collectionView(_:layout:sizeForItemAt:), use a custom variable with the itemSize instead.
    • This works best when you set self.collectionView.decelerationRate = UIScrollView.DecelerationRate.fast.

    Here's a horizontal version (haven't tested it thoroughly so please forgive any mistakes):

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
    
        guard let collectionView = self.collectionView else {
            let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
            return latestOffset
        }
    
        // Page width used for estimating and calculating paging.
        let pageWidth = self.itemSize.width + self.minimumInteritemSpacing
    
        // Make an estimation of the current page position.
        let approximatePage = collectionView.contentOffset.x/pageWidth
    
        // Determine the current page based on velocity.
        let currentPage = velocity.x == 0 ? round(approximatePage) : (velocity.x < 0.0 ? floor(approximatePage) : ceil(approximatePage))
    
        // Create custom flickVelocity.
        let flickVelocity = velocity.x * 0.3
    
        // Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
        let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)
    
        // Calculate newHorizontalOffset.
        let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - collectionView.contentInset.left
    
        return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y)
    }
    

    This code is based on the code I use in my personal project, you can check it out here by downloading it and running the Example target.

提交回复
热议问题