UICollectionView Horizontal Paging not centered

前端 未结 11 1641
感动是毒
感动是毒 2020-12-02 09:32

I have a horizontal scrolling collectionView with each cell the size of the view. When I page through the collectionView it doesn\'t page by cell. The cells aren\'t in the c

相关标签:
11条回答
  • 2020-12-02 09:45

    Swift version of @vlad-che accepted answer:

    extension GoodsViewController: UICollectionViewDelegateFlowLayout {
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    
            return 10
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
            let frameSize = collectionView.frame.size
            return CGSize(width: frameSize.width - 10, height: frameSize.height)
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    
            return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:46

    Being able to have cells that are smaller the collectionView frame with space between the cells allows for hinting to the user that there other cells either side to scroll to which is a big win for UX. But for the centering of the pages doesn't work as expected with each cell progressively becoming more offset as the user scrolls. I've found the following to work well. The centering/snapping animation on each cell is almost invisible to user since it is only tweaking where the collectionView scrolling would end naturally rather than jerking the collectionView to quickly scroll to another indexPath. It's still important to to have the sectionInset property set large enough to allow cell not to stick to the containing frame edges. Also since there are spaces between the cells the target could land on an indexPath of nil which would cause the collectionView to scroll back to the start. I've fixed this offsetting a little and then trying again but different approaches could be taken here.

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
    {
        //Ensure the scrollview is the collectionview we care about
        if (scrollView == self.collectionView) {
    
            // Find cell closest to the frame centre with reference from the targetContentOffset.
            CGPoint frameCentre = self.collectionView.center;
            CGPoint targetOffsetToCentre = CGPointMake((* targetContentOffset).x + frameCentre.x, (* targetContentOffset).y + frameCentre.y);
    
            NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];
    
                //Check for "edgecase" that the target will land between cells and then find a close neighbour to prevent scrolling to index {0,0}.
            while (!indexPath) {
                targetOffsetToCentre.x += ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumInteritemSpacing;
                indexPath = [self.collectionView indexPathForItemAtPoint:targetOffsetToCentre];
            }
    
            // Find the centre of the target cell
            CGPoint centreCellPoint = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath].center;
    
            // Calculate the desired scrollview offset with reference to desired target cell centre.
            CGPoint desiredOffset = CGPointMake(centreCellPoint.x - frameCentre.x, centreCellPoint.y - frameCentre.y);
            *targetContentOffset = desiredOffset;
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:46

    If you're after the behaviour of collectionView.isPagingEnabled (so with 'proper inertia feeling' etc) but without wrong offsets when setting a contentInset or spacing, this is what you need:

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let itemWidth = cellSize.width + spacing
        let inertialTargetX = targetContentOffset.pointee.x
        let offsetFromPreviousPage = (inertialTargetX + collectionView.contentInset.left).truncatingRemainder(dividingBy: itemWidth)
        
        // snap to the nearest page
        let pagedX: CGFloat
        if offsetFromPreviousPage > itemWidth / 2 {
            pagedX = inertialTargetX + (itemWidth - offsetFromPreviousPage)
        } else {
            pagedX = inertialTargetX - offsetFromPreviousPage
        }
        
        let point = CGPoint(x: pagedX, y: targetContentOffset.pointee.y)
        targetContentOffset.pointee = point
    }
    
    0 讨论(0)
  • 2020-12-02 09:49

    Swift 4 solution to remove line spacing to keep cells centered:

    public func collectionView(_ collectionView: UICollectionView, layout 
    collectionViewLayout: UICollectionViewLayout, 
    minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    
    0 讨论(0)
  • 2020-12-02 09:52

    Swift 3

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
            if scrollView == self.collectionView {
                var currentCellOffset = self.collectionView.contentOffset
                currentCellOffset.x += self.collectionView.frame.width / 2
                if let indexPath = self.collectionView.indexPathForItem(at: currentCellOffset) {
                  self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
                }
            }
        }
    
    0 讨论(0)
提交回复
热议问题