UICollectionView水平分页未居中

时间:2015-04-15 18:45:27

标签: ios uiscrollview uicollectionview

我有一个水平滚动collectionView,每个单元格都是视图的大小。当我浏览collectionView时,它不会按单元格进行分页。细胞不在屏幕中央。我已经尝试了很多东西试图解决它并且没有任何运气。 这是一个问题视频: https://www.youtube.com/watch?v=tXsxWelk16w 有什么想法吗?

13 个答案:

答案 0 :(得分:109)

删除项目之间的空格。对于水平滚动集合视图,将最小行间距设置为0.您可以使用接口构建器或UICollectionViewDelegateFlowLayout协议方法执行此操作:

- (CGFloat)collectionView:(UICollectionView *)collectionView 
                   layout:(UICollectionViewLayout *)collectionViewLayout 
        minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 0;    
}

enter image description here

另一种方法是使单元格的宽度小于collectionView的宽度,以获得项目之间水平空间的值。然后使用左右插图添加部分插入,这些插入等于项目之间的水平空间的一半。例如,您的最小行间距为10:

- (CGFloat)collectionView:(UICollectionView *)collectionView
                   layout:(UICollectionViewLayout *)collectionViewLayout
        minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 10;
}

- (CGSize)collectionView:(UICollectionView *)collectionView 
                  layout:(UICollectionViewLayout *)collectionViewLayout 
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake(collectionView.frame.size.width - 10, collectionView.frame.size.height);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView 
                        layout:(UICollectionViewLayout *)collectionViewLayout 
        insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 5, 0, 5);
}

enter image description here

第三种方式:在scrollViewDidEndDecelerating:方法中操作collectionView滚动:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (scrollView == self.collectionView) {
        CGPoint currentCellOffset = self.collectionView.contentOffset;
        currentCellOffset.x += self.collectionView.frame.size.width / 2;
        NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:currentCellOffset];
        [self.collectionView scrollToItemAtIndexPath:indexPath
                                    atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                            animated:YES];
    }
}

enter image description here

答案 1 :(得分:28)

Swift 3 中进行演示: https://github.com/damienromito/CollectionViewCustom

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let pageWidth = Float(itemWidth + itemSpacing)
    let targetXContentOffset = Float(targetContentOffset.pointee.x)
    let contentWidth = Float(collectionView!.contentSize.width  )
    var newPage = Float(self.pageControl.currentPage)

    if velocity.x == 0 {
        newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
    } else {
        newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
        if newPage < 0 {
            newPage = 0
        }
        if (newPage > contentWidth / pageWidth) {
            newPage = ceil(contentWidth / pageWidth) - 1.0
        }
    }
    self.pageControl.currentPage = Int(newPage)
    let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
    targetContentOffset.pointee = point
}

Swift 4

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let pageWidth = Float(itemWidth + itemSpacing)
    let targetXContentOffset = Float(targetContentOffset.pointee.x)
    let contentWidth = Float(collectionView!.contentSize.width  )
    var newPage = Float(self.pageControl.currentPage)

    if velocity.x == 0 {
        newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth)) + 1.0
    } else {
        newPage = Float(velocity.x > 0 ? self.pageControl.currentPage + 1 : self.pageControl.currentPage - 1)
        if newPage < 0 {
            newPage = 0
        }
        if (newPage > contentWidth / pageWidth) {
            newPage = ceil(contentWidth / pageWidth) - 1.0
        }
    }

    self.pageControl.currentPage = Int(newPage)
    let point = CGPoint (x: CGFloat(newPage * pageWidth), y: targetContentOffset.pointee.y)
    targetContentOffset.pointee = point
}

答案 2 :(得分:8)

@ vlad-che的Swift版本接受了答案:

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)
    }
}

答案 3 :(得分:3)

能够让单元格小collectionView的单元格与单元格之间的空间允许向用户暗示其他单元格可以滚动到哪一侧,这是UX的一大胜利。但是,由于每个单元格在用户滚动时逐渐变得更加偏移,因此页面的居中不会按预期工作。我发现以下内容效果很好。每个单元格上的居中/捕捉动画对于用户来说几乎是不可见的,因为它只是调整collectionView滚动会自然结束而不是猛拉collectionView以快速滚动到另一个indexPath。将sectionInset属性设置得足够大以允许单元格不粘附到包含的框架边缘仍然很重要。此外,由于单元格之间存在空格,目标可能会落在indexPath的nil上,这会导致collectionView滚动回到开头。我已经修正了这个偏移了一点然后再试一次但是这里可以采取不同的方法。

- (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;
    }
}

答案 4 :(得分:2)

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)
            }
        }
    }

答案 5 :(得分:2)

Swift 4解决方案,以消除行间距,以保持细胞居中:

public func collectionView(_ collectionView: UICollectionView, layout 
collectionViewLayout: UICollectionViewLayout, 
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

答案 6 :(得分:1)

我刚从Apple官方指南和示例代码中看到的代码:

  

AssetViewController.swift:

self.collectionView?.isPagingEnabled = true
self.collectionView?.frame = view.frame.insetBy(dx: -20.0, dy: 0.0)

答案 7 :(得分:0)

Swift 3.0设置您自己的UICollectionViewFlowLayout

let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let width = UIScreen.main.bounds.width
layout.itemSize = CGSize(width: width, height: 154)
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.scrollDirection = .horizontal
collectionView?.collectionViewLayout = layout

答案 8 :(得分:0)

在遇到类似问题后,我通过认识到使用水平滚动时高度现在是宽度而宽度现在是高度,因为默认设置为垂直滚动。尝试切换值,看看是否有帮助。 https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html

答案 9 :(得分:0)

如果您遵循collectionView.isPagingEnabled的行为(因此具有“适当的惯性感觉”等),但是在设置contentInset或interval时没有错误的偏移量,这就是您需要的:

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
}

答案 10 :(得分:0)

这里的问题是 isPagingEnabled 不考虑 contentSize,而是考虑集合边界:

<块引用>

当用户滚动时,滚动视图会在滚动视图边界的倍数处停止。

为了实现我们想要的,我们需要自己计算下一个偏移量并关闭isPagingEnabled。

enter image description here

如上图所示,第二个单元格应从以下位置开始:

One side + Cell - Next cell visible part

这是一个计算的委托: https://gist.github.com/danielCarlosCE/7a5f80dc6087773ba147be4dc72da826

答案 11 :(得分:0)

将集合视图添加到全屏并删除单元格之间的间距和估计大小将无

enter image description here

添加这个集合视图委托方法

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: collectionView.frame.width, height: collectionView.frame.height) }

答案 12 :(得分:-1)

Swift 3解决方案基于@Santos的回答,如果你有一个常规的水平分页收集视图没有像Paolo在他的Swift 3中使用的页面控件那么使用例。

我用它来解决一个问题,其中水平分页单元格全屏幕单元格与自定义UICollectionViewFlowLayout动画师没有完成旋转并最终偏移,以便全屏幕单元格框架的边缘逐渐水平偏离设置滚动时的集合视图边界(如视频OP共享)。

 func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    // Ensure the scrollview is the one on the collectionView we care are working with 
    if (scrollView == self.collectionView) {

        // Find cell closest to the frame centre with reference from the targetContentOffset.
        let frameCenter: CGPoint = self.collectionView.center
        var targetOffsetToCenter: CGPoint = CGPoint(x: targetContentOffset.pointee.x + frameCenter.x, y: targetContentOffset.pointee.y + frameCenter.y)
        var indexPath: IndexPath? = self.collectionView.indexPathForItem(at: targetOffsetToCenter)

        // Check for "edge case" where the target will land right between cells and then next neighbor to prevent scrolling to index {0,0}.
        while indexPath == nil {
            targetOffsetToCenter.x += 10
            indexPath = self.collectionView.indexPathForItem(at: targetOffsetToCenter)
        }
        // safe unwrap to make sure we found a valid index path
        if let index = indexPath { 
            // Find the centre of the target cell
            if let centerCellPoint: CGPoint = collectionView.layoutAttributesForItem(at: index)?.center {

                // Calculate the desired scrollview offset with reference to desired target cell centre.
                let desiredOffset: CGPoint = CGPoint(x: centerCellPoint.x - frameCenter.x, y: centerCellPoint.y - frameCenter.y)
                targetContentOffset.pointee = desiredOffset
            }
        }
    }
}