簡體   English   中英

UICollectionView 水平分頁 3 項

[英]UICollectionView horizontal paging with 3 items

我需要在 UICollectionView 中顯示 3 個項目,像這樣啟用分頁

在此處輸入圖像描述

但我越來越像這樣在此處輸入圖像描述

我已經制作了自定義流程,並且啟用了分頁但無法獲得我需要的內容。 我怎樣才能做到這一點,或者我應該調查哪個代表,或者將我引導到某個鏈接,從那里我可以獲得有關這種情況的幫助。

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

編輯:演示鏈接: https : //github.com/raheelsadiq/UICollectionView-horizo​​ntal-paging-with-3-items

經過大量搜索后,我找到了下一個要滾動到的點並禁用分頁。 在 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];
}

我還必須使左右小而中心大,所以我用變換來做到這一點。 問題是找到索引,所以很難找到。

對於相同方法中的左右變換,請使用 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;
    }];
}

並在 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 
}

添加這兩個宏或者你想同時處理這兩個宏

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

最終結果是

在此處輸入圖片說明

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

    }

基於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 的回答很好,但我認為很難理解。 在我看來,這是一個可讀性很強的版本:

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

您必須覆蓋流布局的targetContentOffsetForProposedContentOffset:withScrollingVelocity:方法。 這樣你就可以捕捉滾動視圖的停止點。

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

您還需要設置流布局的 sectionInset 以將第一個單元格和最后一個單元格居中。 我的例子是高度但很容易切換到寬度。

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

水平集合視圖分頁的我的解決方案

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

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

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

我有前導和尾隨填充為 15px 的集合視圖單元格,並且面臨分頁問題,所以我解決了它覆蓋 scrollViewWillEndDragging。 您可以使用以下 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