ios - uicollectionview objective c




按單元分頁UICollectionView,而不是屏幕 (8)

方法1:收集視圖

flowLayoutUICollectionViewFlowLayout屬性

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

    if let collectionView = collectionView {

        targetContentOffset.memory = scrollView.contentOffset
        let pageWidth = CGRectGetWidth(scrollView.frame) + flowLayout.minimumInteritemSpacing

        var assistanceOffset : CGFloat = pageWidth / 3.0

        if velocity.x < 0 {
            assistanceOffset = -assistanceOffset
        }

        let assistedScrollPosition = (scrollView.contentOffset.x + assistanceOffset) / pageWidth

        var targetIndex = Int(round(assistedScrollPosition))


        if targetIndex < 0 {
            targetIndex = 0
        }
        else if targetIndex >= collectionView.numberOfItemsInSection(0) {
            targetIndex = collectionView.numberOfItemsInSection(0) - 1
        }

        print("targetIndex = \(targetIndex)")

        let indexPath = NSIndexPath(forItem: targetIndex, inSection: 0)

        collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: true)
    }
}

方法2:頁面視圖控制器

如果符合您的要求,您可以使用UIPageViewController ,每個頁面都有獨立的視圖控制器。

我有UICollectionView水平滾動,並且每個整個屏幕總是有2個單元格並排。 我需要滾動停止在一個單元格的開始。 啟用分頁功能後,集合視圖會一次滾動整個頁面,即2個單元格,然後停止。

我需要啟用單個單元格的滾動,或者停止在單元格邊緣的多個單元格滾動。

我試圖UICollectionViewFlowLayout並實現方法targetContentOffsetForProposedContentOffset ,但到目前為止我只能打破我的集合視圖,並停止滾動。 有沒有更簡單的方法來實現這一點,或者我是否真的需要實現UICollectionViewFlowLayout子類的所有方法? 謝謝。


使用自定義頁面寬度進行水平分頁(Swift 4)

這裡提出的許多解決方案導致一些奇怪的行為,感覺不像正確實施分頁。

但是, 本教程中介紹的解決方案似乎沒有任何問題。 它只是一個完美的分頁算法。 您可以通過5個簡單的步驟來實現它:

  1. 將以下屬性添加到您的類型中: private var indexOfCellBeforeDragging = 0
  2. 像這樣設置collectionView delegatecollectionView.delegate = self
  3. 通過擴展添加一致性到UICollectionViewDelegateextension YourType: UICollectionViewDelegate { }
  4. 將下列方法添加到實現UICollectionViewDelegate一致性的擴展中,並為pageWidth設置一個值:

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        let pageWidth = // The width your page should have (plus a possible margin)
        let proportionalOffset = collectionView.contentOffset.x / pageWidth
        indexOfCellBeforeDragging = Int(round(proportionalOffset))
    }
    
  5. 將下面的方法添加到實現UICollectionViewDelegate一致性的擴展中,為pageWidth設置相同的值(您也可以將此值存儲在中心位置)並設置collectionViewItemCount的值:

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        // Stop scrolling
        targetContentOffset.pointee = scrollView.contentOffset
    
        // Calculate conditions
        let pageWidth = // The width your page should have (plus a possible margin)
        let collectionViewItemCount = // The number of items in this section
        let proportionalOffset = collectionView.contentOffset.x / pageWidth
        let indexOfMajorCell = Int(round(proportionalOffset))
        let swipeVelocityThreshold: CGFloat = 0.5
        let hasEnoughVelocityToSlideToTheNextCell = indexOfCellBeforeDragging + 1 < collectionViewItemCount && velocity.x > swipeVelocityThreshold
        let hasEnoughVelocityToSlideToThePreviousCell = indexOfCellBeforeDragging - 1 >= 0 && velocity.x < -swipeVelocityThreshold
        let majorCellIsTheCellBeforeDragging = indexOfMajorCell == indexOfCellBeforeDragging
        let didUseSwipeToSkipCell = majorCellIsTheCellBeforeDragging && (hasEnoughVelocityToSlideToTheNextCell || hasEnoughVelocityToSlideToThePreviousCell)
    
        if didUseSwipeToSkipCell {
            // Animate so that swipe is just continued
            let snapToIndex = indexOfCellBeforeDragging + (hasEnoughVelocityToSlideToTheNextCell ? 1 : -1)
            let toValue = pageWidth * CGFloat(snapToIndex)
            UIView.animate(
                withDuration: 0.3,
                delay: 0,
                usingSpringWithDamping: 1,
                initialSpringVelocity: velocity.x,
                options: .allowUserInteraction,
                animations: {
                scrollView.contentOffset = CGPoint(x: toValue, y: 0)
                scrollView.layoutIfNeeded()
            }, completion: nil)
        } else {
            // Pop back (against velocity)
            let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
            collectionView.scrollToItem(at: indexPath, at: .left, animated: true)
        }
    }
    

你也可以創建假滾動視圖來處理滾動。

水平或垂直

// === Defaults ===
let bannerSize = CGSize(width: 280, height: 170)
let pageWidth: CGFloat = 290 // ^ + paging
let insetLeft: CGFloat = 20
let insetRight: CGFloat = 20
// ================

var pageScrollView: UIScrollView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Create fake scrollview to properly handle paging
    pageScrollView = UIScrollView(frame: CGRect(origin: .zero, size: CGSize(width: pageWidth, height: 100)))
    pageScrollView.isPagingEnabled = true
    pageScrollView.alwaysBounceHorizontal = true
    pageScrollView.showsVerticalScrollIndicator = false
    pageScrollView.showsHorizontalScrollIndicator = false
    pageScrollView.delegate = self
    pageScrollView.isHidden = true
    view.insertSubview(pageScrollView, belowSubview: collectionView)

    // Set desired gesture recognizers to the collection view
    for gr in pageScrollView.gestureRecognizers! {
        collectionView.addGestureRecognizer(gr)
    }
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView == pageScrollView {
        // Return scrolling back to the collection view
        collectionView.contentOffset.x = pageScrollView.contentOffset.x
    }
}

func refreshData() {
    ...

    refreshScroll()
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    refreshScroll()
}

/// Refresh fake scrolling view content size if content changes
func refreshScroll() {
    let w = collectionView.width - bannerSize.width - insetLeft - insetRight
    pageScrollView.contentSize = CGSize(width: pageWidth * CGFloat(banners.count) - w, height: 100)
}

只需重寫該方法:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    *targetContentOffset = scrollView.contentOffset; // set acceleration to 0.0
    float pageWidth = (float)self.articlesCollectionView.bounds.size.width;
    int minSpace = 10;

    int cellToSwipe = (scrollView.contentOffset.x)/(pageWidth + minSpace) + 0.5; // cell width + min spacing for lines
    if (cellToSwipe < 0) {
        cellToSwipe = 0;
    } else if (cellToSwipe >= self.articles.count) {
        cellToSwipe = self.articles.count - 1;
    }
    [self.articlesCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:cellToSwipe inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}

對於尋找基於垂直單元的分頁的人來說(因為這正是我最初尋找的)。下面是我正在構建的library的一部分代碼(您需要將它放在UICollectionViewFlowLayout的子類中)。

您也可以將其更改為水平工作。

碼:

/** This property enables paging per card. The default value is true. */
public var isPagingEnabled: Bool = true

// MARK: cell paging
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    // MARK: If the property `isPagingEnabled` is set to false, we don't enable paging and thus return the current contentoffset
    guard isPagingEnabled else {
        let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        return latestOffset
    }

    var offsetAdjustment = CGFloat.greatestFiniteMagnitude
    let verticalOffset = proposedContentOffset.y
    let contentInset = collectionView!.contentInset.top

    let targetRect = CGRect(origin: CGPoint(x: 0, y: verticalOffset), size: self.collectionView!.bounds.size)

    for layoutAttributes in super.layoutAttributesForElements(in: targetRect)! {
        // MARK: we adjust for the contentInset
        let itemOffset = layoutAttributes.frame.origin.y - contentInset
        if (abs(itemOffset - verticalOffset) < abs(offsetAdjustment)) {
            offsetAdjustment = itemOffset - verticalOffset
        }
    }
    return CGPoint(x: proposedContentOffset.x, y: verticalOffset + offsetAdjustment)
}

有點像evya的答案,但有點平滑,因為它不會將targetContentOffset設置為零。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if ([scrollView isKindOfClass:[UICollectionView class]]) {
        UICollectionView* collectionView = (UICollectionView*)scrollView;
        if ([collectionView.collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]) {
            UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*)collectionView.collectionViewLayout;

            CGFloat pageWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
            CGFloat usualSideOverhang = (scrollView.bounds.size.width - pageWidth)/2.0;
            // k*pageWidth - usualSideOverhang = contentOffset for page at index k if k >= 1, 0 if k = 0
            // -> (contentOffset + usualSideOverhang)/pageWidth = k at page stops

            NSInteger targetPage = 0;
            CGFloat currentOffsetInPages = (scrollView.contentOffset.x + usualSideOverhang)/pageWidth;
            targetPage = velocity.x < 0 ? floor(currentOffsetInPages) : ceil(currentOffsetInPages);
            targetPage = MAX(0,MIN(self.projects.count - 1,targetPage));

            *targetContentOffset = CGPointMake(MAX(targetPage*pageWidth - usualSideOverhang,0), 0);
        }
    }
}

這是我在Swift 3中的版本。計算滾動結束後的偏移量,並用動畫調整偏移量。

collectionLayout是一個UICollectionViewFlowLayout()

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let index = scrollView.contentOffset.x / collectionLayout.itemSize.width
    let fracPart = index.truncatingRemainder(dividingBy: 1)
    let item= Int(fracPart >= 0.5 ? ceil(index) : floor(index))

    let indexPath = IndexPath(item: item, section: 0)
    collectionView.scrollToItem(at: indexPath, at: .left, animated: true)
}

部分基於StevenOjo的回答。 我已經測試了這個使用水平滾動和沒有Bounce UICollectionView。 cellSize是CollectionViewCell大小。 您可以調整因子來修改滾動靈敏度。

override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
    var factor: CGFloat = 0.5
    if velocity.x < 0 {
        factor = -factor
    }
    let indexPath = IndexPath(row: (scrollView.contentOffset.x/cellSize.width + factor).int, section: 0)
    collectionView?.scrollToItem(at: indexPath, at: .left, animated: true)
}




uicollectionviewlayout