Ios UICollectionView水平分页未居中

Ios UICollectionView水平分页未居中,ios,uiscrollview,uicollectionview,Ios,Uiscrollview,Uicollectionview,我有一个水平滚动的collectionView,每个单元格都有视图的大小。当我在collectionView中翻页时,它不会逐单元格翻页。单元格不在屏幕中央。我已经尝试了很多方法来修复它,但没有任何运气。 下面是一段关于这个问题的视频: 有什么想法吗?删除项目之间的空格。对于水平滚动集合视图,将最小行距设置为0。您可以使用interface builder或使用UICollectionViewDelegateFlowLayout协议的方法执行此操作: - (CGFloat)collectionV

我有一个水平滚动的collectionView,每个单元格都有视图的大小。当我在collectionView中翻页时,它不会逐单元格翻页。单元格不在屏幕中央。我已经尝试了很多方法来修复它,但没有任何运气。 下面是一段关于这个问题的视频:
有什么想法吗?

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

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

另一种方法是使单元格的宽度小于项目之间水平间距值的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);
}

第三种方法:在
ScrollViewDiEndDecelling:
method:

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

能够在
collectionView
框架中使用更小的单元格,并且单元格之间留有空间,这样就可以向用户暗示,两边都有其他单元格可以滚动,这是用户体验的一大胜利。但是,页面的居中并不像预期的那样工作,随着用户滚动,每个单元格的偏移量逐渐增大。我发现以下方法很有效。用户几乎看不到每个单元格上的居中/捕捉动画,因为它只是调整
collectionView
滚动自然结束的位置,而不是猛拉
collectionView
快速滚动到另一个
indexPath
。将
sectionInset
属性设置得足够大,以允许单元格不粘附到包含的帧边缘,这一点仍然很重要。此外,由于单元格之间有空格,目标可能会降落在一个为nil的
indepath
上,这将导致
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;
    }
}

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

雨燕3中的演示:

func-scrollViewWillendDraging(scrollView:UIScrollView,withVelocity:CGPoint,targetContentOffset:UnsafemeutablePointer){
让pageWidth=浮动(itemWidth+itemSpacing)
让targetXContentOffset=Float(targetContentOffset.pointee.x)
让contentWidth=Float(collectionView!.contentSize.width)
var newPage=Float(self.pageControl.currentPage)
如果速度.x==0{
新建页面=地板((targetXContentOffset-浮动(页面宽度)/2)/浮动(页面宽度))+1.0
}否则{
newPage=Float(velocity.x>0?self.pageControl.currentPage+1:self.pageControl.currentPage-1)
如果newPage<0{
newPage=0
}
如果(新建页面>内容宽度/页面宽度){
newPage=ceil(contentWidth/pageWidth)-1.0
}
}
self.pageControl.currentPage=Int(newPage)
设point=CGPoint(x:CGFloat(newPage*pageWidth),y:targetContentOffset.pointee.y)
targetContentOffset.pointee=点
}
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
}
func-scrollViewWillendDraging(scrollView:UIScrollView,withVelocity:CGPoint,targetContentOffset:UnsafemeutablePointer){
让pageWidth=浮动(itemWidth+itemSpacing)
让targetXContentOffset=Float(targetContentOffset.pointee.x)
让contentWidth=Float(collectionView!.contentSize.width)
var newPage=Float(self.pageControl.currentPage)
如果速度.x==0{
新建页面=地板((targetXContentOffset-浮动(页面宽度)/2)/浮动(页面宽度))+1.0
}否则{
newPage=Float(velocity.x>0?self.pageControl.currentPage+1:self.pageControl.currentPage-1)
如果newPage<0{
newPage=0
}
如果(新建页面>内容宽度/页面宽度){
newPage=ceil(contentWidth/pageWidth)-1.0
}
}
self.pageControl.currentPage=Int(newPage)
设point=CGPoint(x:CGFloat(newPage*pageWidth),y:targetContentOffset.pointee.y)
targetContentOffset.pointee=点
}

在遇到类似问题后,我修复了我的问题,因为我意识到使用水平滚动时,高度现在是宽度,宽度现在是高度,因为默认设置为垂直滚动。尝试切换这些值,看看是否有帮助。

Swift 3解决方案基于@Santos的答案,如果您有一个常规的水平分页集合视图,而没有页面控件,则可以使用,就像Paolo在他的Swift 3示例中使用的那样

我用它来解决一个问题,即带有自定义UICollectionViewFlowLayout动画师的水平分页单元格全屏单元格没有完成旋转,并最终偏移,因此当您滚动时,全屏单元格帧的边缘越来越水平地偏离集合视图的边界(如在视频OP共享中)

func-scrollViewWillendDraging(scrollView:UIScrollView,withVelocity:CGPoint,targetContentOffset:UnsafemeutablePointer){
//确保scrollview是我们所关心的collectionView中的一个
if(scrollView==self.collectionView){
//从targetContentOffset中查找最靠近帧中心的单元格。
让frameCenter:CGPoint=self.collectionView.center
var targetOffsetToCenter:CGPoint=CGPoint(x:targetContentOffset.pointee.x+frameCenter.x,y:targetContentOffset.pointee.y+frameCenter.y)
变量indexPath:indexPath?=self.collectionView.indexPathForItem(位于:targetOffsetToCenter)
//检查“边缘情况”,目标将正好落在单元格和下一个相邻单元格之间,以防止滚动到索引{0,0}。
而indexPath==nil{
targetOffsetToCenter.x+=10
indexPath=self.collection
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
}
 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
            }
        }
    }
}
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)
            }
        }
    }
self.collectionView?.isPagingEnabled = true
self.collectionView?.frame = view.frame.insetBy(dx: -20.0, dy: 0.0)
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)
    }
}
public func collectionView(_ collectionView: UICollectionView, layout 
collectionViewLayout: UICollectionViewLayout, 
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}
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
}