简体   繁体   English

UICollectionView 水平分页 3 项

[英]UICollectionView horizontal paging with 3 items

I need to show 3 items in a UICollectionView, with paging enabled like this我需要在 UICollectionView 中显示 3 个项目,像这样启用分页

在此处输入图像描述

but I am getting like this但我越来越像这样在此处输入图像描述

I have made custom flow, plus paging is enabled but not able to get what i need.我已经制作了自定义流程,并且启用了分页但无法获得我需要的内容。 How can i achieve this or which delegate should i look into, or direct me to some link from where i can get help for this scenario.我怎样才能做到这一点,或者我应该调查哪个代表,或者将我引导到某个链接,从那里我可以获得有关这种情况的帮助。

- (void)awakeFromNib
{
    self.itemSize = CGSizeMake(480, 626);
    self.minimumInteritemSpacing = 112;
    self.minimumLineSpacing = 112;
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.sectionInset = UIEdgeInsetsMake(0, 272, 0, 272);
}

Edit : Demo link: https://github.com/raheelsadiq/UICollectionView-horizontal-paging-with-3-items编辑:演示链接: https : //github.com/raheelsadiq/UICollectionView-horizo​​ntal-paging-with-3-items

After a lot searching I did it, find the next point to scroll to and disable the paging.经过大量搜索后,我找到了下一个要滚动到的点并禁用分页。 In scrollviewWillEndDragging scroll to next cell x.在 scrollviewWillEndDragging 中滚动到下一个单元格 x。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{

    float pageWidth = 480 + 50; // width + space

    float currentOffset = scrollView.contentOffset.x;
    float targetOffset = targetContentOffset->x;
    float newTargetOffset = 0;

    if (targetOffset > currentOffset)
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
    else
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;

    if (newTargetOffset < 0)
        newTargetOffset = 0;
    else if (newTargetOffset > scrollView.contentSize.width)
        newTargetOffset = scrollView.contentSize.width;

    targetContentOffset->x = currentOffset;
    [scrollView setContentOffset:CGPointMake(newTargetOffset, scrollView.contentOffset.y) animated:YES];
}

I also had to make the left and right small and center large, so i did it with transform.我还必须使左右小而中心大,所以我用变换来做到这一点。 The issue was finding the index, so that was very difficult to find.问题是找到索引,所以很难找到。

For transform left and right in this same method use the newTargetOffset对于相同方法中的左右变换,请使用 newTargetOffset

int index = newTargetOffset / pageWidth;

if (index == 0) { // If first index 
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index  inSection:0]];

    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = CGAffineTransformIdentity;
    }];
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index + 1  inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];
}else{
    UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = CGAffineTransformIdentity;
    }];

    index --; // left
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];

    index ++;
    index ++; // right
    cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
    [UIView animateWithDuration:ANIMATION_SPEED animations:^{
        cell.transform = TRANSFORM_CELL_VALUE;
    }];
}

And in cellForRowAtIndex add并在 cellForRowAtIndex 添加

if (indexPath.row == 0 && isfirstTimeTransform) { // make a bool and set YES initially, this check will prevent fist load transform
    isfirstTimeTransform = NO;
}else{
    cell.transform = TRANSFORM_CELL_VALUE; // the new cell will always be transform and without animation 
}

Add these two macros too or as u wish to handle both添加这两个宏或者你想同时处理这两个宏

#define TRANSFORM_CELL_VALUE CGAffineTransformMakeScale(0.8, 0.8)
#define ANIMATION_SPEED 0.2

The end result is最终结果是

在此处输入图片说明

Part one of @Raheel Sadiq answer in Swift 3 , without Transform. @Raheel SadiqSwift 3 中回答的第一部分,没有转换。

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let pageWidth: Float = Float(self.collectionView.frame.width / 3) //480 + 50
        // width + space
        let currentOffset: Float = Float(scrollView.contentOffset.x)
        let targetOffset: Float = Float(targetContentOffset.pointee.x)
        var newTargetOffset: Float = 0
        if targetOffset > currentOffset {
            newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
        }
        else {
            newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
        }
        if newTargetOffset < 0 {
            newTargetOffset = 0
        }
        else if (newTargetOffset > Float(scrollView.contentSize.width)){
            newTargetOffset = Float(Float(scrollView.contentSize.width))
        }

        targetContentOffset.pointee.x = CGFloat(currentOffset)
        scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)

    }

Swift 3.0 Complete Solution based on Raheel Sadiq基于Raheel Sadiq 的Swift 3.0完整解决方案

var isfirstTimeTransform:Bool = true

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {


    let cell : UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCustomViewCell", for: indexPath)

    if (indexPath.row == 0 && isfirstTimeTransform) { 
        isfirstTimeTransform = false
    }else{
        cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 
    }

    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    return CGSize(width: collectionView.bounds.width/3, height: collectionView.bounds.height)
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    // Simulate "Page" Function
    let pageWidth: Float = Float(self.collectionView.frame.width/3 + 20)
    let currentOffset: Float = Float(scrollView.contentOffset.x)
    let targetOffset: Float = Float(targetContentOffset.pointee.x)
    var newTargetOffset: Float = 0
    if targetOffset > currentOffset {
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
    }
    else {
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
    }
    if newTargetOffset < 0 {
        newTargetOffset = 0
    }
    else if (newTargetOffset > Float(scrollView.contentSize.width)){
        newTargetOffset = Float(Float(scrollView.contentSize.width))
    }

    targetContentOffset.pointee.x = CGFloat(currentOffset)
    scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)

    // Make Transition Effects for cells
    let duration = 0.2
    var index = newTargetOffset / pageWidth;
    var cell:UICollectionViewCell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
    if (index == 0) { // If first index
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform.identity
        }, completion: nil)
        index += 1
        cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        }, completion: nil)
    }else{
        UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
            cell.transform = CGAffineTransform.identity;
        }, completion: nil)

        index -= 1 // left
        if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
            }, completion: nil)
        }

        index += 1
        index += 1 // right
        if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
            }, completion: nil)
        }
    }

}

@raheel-sadiq answer is great but pretty hard to understand, I think. @raheel-sadiq 的回答很好,但我认为很难理解。 Here's a much readable version, in my opinion:在我看来,这是一个可读性很强的版本:

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

        //minimumLineSpacing and insetForSection are two constants in my code

        //this cell width is for my case, adapt to yours
        let cellItemWidth = view.frame.width - (insetForSection.left + insetForSection.right)
        let pageWidth = Float(cellItemWidth + minimumLineSpacing)

        let offsetXAfterDragging = Float(scrollView.contentOffset.x)
        let targetOffsetX = Float(targetContentOffset.pointee.x)

        let pagesCountForOffset = pagesCount(forOffset: offsetXAfterDragging, withTargetOffset: targetOffsetX, pageWidth: pageWidth)

        var newTargetOffsetX = pagesCountForOffset * pageWidth

        keepNewTargetInBounds(&newTargetOffsetX, scrollView)

        //ignore target
        targetContentOffset.pointee.x = CGFloat(offsetXAfterDragging)

        let newTargetPoint = CGPoint(x: CGFloat(newTargetOffsetX), y: scrollView.contentOffset.y)
        scrollView.setContentOffset(newTargetPoint, animated: true)

        //if you're using pageControl
        pageControl.currentPage = Int(newTargetOffsetX / pageWidth)

    }

    fileprivate func pagesCount(forOffset offset: Float, withTargetOffset targetOffset: Float, pageWidth: Float) -> Float {
        let isRightDirection = targetOffset > offset
        let roundFunction = isRightDirection ? ceilf : floorf
        let pagesCountForOffset = roundFunction(offset / pageWidth)
        return pagesCountForOffset
    }

    fileprivate func keepNewTargetInBounds(_ newTargetOffsetX: inout Float, _ scrollView: UIScrollView) {
        if newTargetOffsetX < 0 { newTargetOffsetX = 0 }
        let contentSizeWidth = Float(scrollView.contentSize.width)
        if newTargetOffsetX > contentSizeWidth { newTargetOffsetX = contentSizeWidth }
    }

you will have to override targetContentOffsetForProposedContentOffset:withScrollingVelocity: method of the flow layout.您必须覆盖流布局的targetContentOffsetForProposedContentOffset:withScrollingVelocity:方法。 This way you snap the stopping point of the scrollview.这样你就可以捕捉滚动视图的停止点。

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    CGFloat yOffset = MAXFLOAT;

    CGRect proposedRect;
    proposedRect.origin = proposedContentOffset;
    proposedRect.size = self.collectionView.bounds.size;
    CGPoint proposedCenterPoint = CGPointMake(CGRectGetMidX(proposedRect), CGRectGetMidY(proposedRect)) ;

    NSArray *array = [super layoutAttributesForElementsInRect:proposedRect];

    for (UICollectionViewLayoutAttributes *attributes in array)
    {
        CGFloat newOffset = attributes.center.y - proposedCenterPoint.y;
        if ( fabsf(newOffset) < fabs(yOffset))
        {
            yOffset = newOffset;
        }
    }

    return CGPointMake(proposedContentOffset.x, proposedContentOffset.y + yOffset);
}

Also you will beed to set the sectionInset of the flow layout to center the first cell and the last cell.您还需要设置流布局的 sectionInset 以将第一个单元格和最后一个单元格居中。 My example is the height but easy to switch to width.我的例子是高度但很容易切换到宽度。

CGFloat height = (self.collectionView.bounds.size.height / 2.0 ) - (self.itemSize.height / 2.0) ;
self.sectionInset = UIEdgeInsetsMake(height, 30.0, height, 30.0) ;

Mine solution to horizontal collection view paging水平集合视图分页的我的解决方案

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if scrollView == collectionView { collectionView.scrollToPage() }
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if scrollView == collectionView { if !decelerate { collectionView.scrollToPage() } }
}

And small extension to collectionView和 collectionView 的小扩展

public func scrollToPage() {
        var currentCellOffset = contentOffset
        currentCellOffset.x += width / 2
        var path = indexPathForItem(at: currentCellOffset)
        if path.isNil {
            currentCellOffset.x += 15
            path = indexPathForItem(at: currentCellOffset)
        }
        if path != nil {
            logInfo("Scrolling to page \(path!)")
            scrollToItem(at: path!, at: .centeredHorizontally, animated: true)
        }
    }

I am having collection view cell with leading and trailing padding of 15px and was facing paging issue, so I resolved it overriding scrollViewWillEndDragging.我有前导和尾随填充为 15px 的集合视图单元格,并且面临分页问题,所以我解决了它覆盖 scrollViewWillEndDragging。 You can use below function as:您可以使用以下 function 作为:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    
    let pageWidth: Float = Float(UIScreen.main.bounds.size.width)

    let currentOffset = Float(scrollView.contentOffset.x)
    let targetOffset: Float = Float(targetContentOffset.pointee.x)
    var newTargetOffset: Float = 0

    if targetOffset > currentOffset {
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
    } else {
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
    }

    if newTargetOffset < 0 {
        newTargetOffset = 0
    } else if CGFloat(newTargetOffset) > scrollView.contentSize.width {
        newTargetOffset = Float(scrollView.contentSize.width)
    }

    targetContentOffset.pointee.x = CGFloat(currentOffset)
    let index = Int(newTargetOffset / pageWidth)
    if index != 0 {
        let spacingForCell:Float = 15
        scrollView.setContentOffset(CGPoint(x: CGFloat( newTargetOffset - spacingForCell*Float(index)), y: 0), animated: true)
    } else {
        scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: 0), animated: true)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM