按单元分页UICollectionView,而不是屏幕

我有UICollectionView水平滚动,并且总是有2个单元并排整个屏幕。 我需要滚动停止在一个单元格的开始。 启用分页后,集合视图会一次滚动整个页面,即2个单元格,然后停止。

我需要启用单个单元格的滚动,或者停止在单元格边缘的多个单元格的滚动。

我试图UICollectionViewFlowLayout并实现方法targetContentOffsetForProposedContentOffset ,但到目前为止,我只能打破我的集合视图,并停止滚动。 有没有更简单的方法来实现这个和如何,或者我真的需要实现UICollectionViewFlowLayout子类的所有方法? 谢谢。

好的,所以我在这里find了解决scheme: targetContentOffsetForProposedContentOffset:withScrollingVelocity without subclassing UICollectionViewFlowLayout

我应该在开始时searchtargetContentOffsetForProposedContentOffset

只需重写该方法:

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

方法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如果它符合你的要求,每个页面将有一个单独的视图控制器。

Swya 3版本的Evya的回答是:

 func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { targetContentOffset.pointee = scrollView.contentOffset let pageWidth:Float = Float(self.view.bounds.width) let minSpace:Float = 10.0 var cellToSwipe:Double = Double(Float((scrollView.contentOffset.x))/Float((pageWidth+minSpace))) + Double(0.5) if cellToSwipe < 0 { cellToSwipe = 0 } else if cellToSwipe >= Double(self.articles.count) { cellToSwipe = Double(self.articles.count) - Double(1) } let indexPath:IndexPath = IndexPath(row: Int(cellToSwipe), section:0) self.collectionView.scrollToItem(at:indexPath, at: UICollectionViewScrollPosition.left, animated: true) } 

部分基于StevenOjo的答案。 我已经testing了这个使用水平滚动和没有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) } 

这是我在Swift中的版本3.计算滚动结束后的偏移量,并用animation调整偏移量。

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

有点像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); } } } 

对于寻找基于垂直单元的分页的人来说(因为这正是我最初寻找的)。下面是我正在构build的库中的一部分代码(您需要将它放在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) }