我有UICollectionView
横向滚动,并且每个屏幕总共有2个单元格并排。我需要滚动才能在单元格的开头停止。启用分页后,集合视图会滚动整个页面,一次是2个单元格,然后停止。
我需要启用单个单元格的滚动,或者在单元格的边缘停止滚动多个单元格。
我尝试继承UICollectionViewFlowLayout
并实现方法targetContentOffsetForProposedContentOffset
,但到目前为止我只能破坏我的集合视图并停止滚动。有没有更简单的方法来实现这个以及如何,或者我真的需要实现UICollectionViewFlowLayout
子类的所有方法?感谢。
答案 0 :(得分:39)
好的,所以我在这里找到了解决方案:targetContentOffsetForProposedContentOffset:withScrollingVelocity without subclassing UICollectionViewFlowLayout
我应该在开始时搜索targetContentOffsetForProposedContentOffset
。
答案 1 :(得分:22)
只需覆盖方法:
- (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];
}
答案 2 :(得分:12)
Evift 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)
}
答案 3 :(得分:8)
部分基于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)
}
答案 4 :(得分:6)
flowLayout
是UICollectionViewFlowLayout
属性
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)
}
}
如果符合您的要求,您可以使用UIPageViewController
,每个页面都有一个单独的视图控制器。
答案 5 :(得分:5)
这里介绍的许多解决方案都会导致一些奇怪的行为,感觉不适合实现正确的分页。
然而,this tutorial中提出的解决方案似乎没有任何问题。它只是一个完美的分页算法。您可以通过5个简单步骤实现它:
private var indexOfCellBeforeDragging = 0
collectionView
delegate
:collectionView.delegate = self
UICollectionViewDelegate
extension YourType: UICollectionViewDelegate { }
一致性
将以下方法添加到实现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))
}
将以下方法添加到实现UICollectionViewDelegate
一致性的扩展中,为pageWidth
设置相同的值(您也可以将此值存储在中心位置)并为{设置值} {1}}:
collectionViewItemCount
答案 6 :(得分:3)
这是一种直接的方法。
案例很简单,但最后很常见(典型的缩略图卷轴具有固定的单元格大小和单元格之间固定的间隙)
var itemCellSize: CGSize = <your cell size>
var itemCellsGap: CGFloat = <gap in between>
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let pageWidth = (itemCellSize.width + itemCellsGap)
let itemIndex = (targetContentOffset.pointee.x) / pageWidth
targetContentOffset.pointee.x = round(itemIndex) * pageWidth - (itemCellsGap / 2)
}
// CollectionViewFlowLayoutDelegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return itemCellSize
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return itemCellsGap
}
请注意,没有理由调用scrollToOffset或深入布局。 原生滚动行为已经完成了所有工作。
干杯全部:)
答案 7 :(得分:3)
修改Romulo BM答案以进行速度聆听
func scrollViewWillEndDragging(
_ scrollView: UIScrollView,
withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>
) {
targetContentOffset.pointee = scrollView.contentOffset
var indexes = collection.indexPathsForVisibleItems
indexes.sort()
var index = indexes.first!
if velocity.x > 0 {
index.row += 1
} else if velocity.x == 0 {
let cell = self.collection.cellForItem(at: index)!
let position = self.collection.contentOffset.x - cell.frame.origin.x
if position > cell.frame.size.width / 2 {
index.row += 1
}
}
self.collection.scrollToItem(at: index, at: .centeredHorizontally, animated: true )
}
答案 8 :(得分:3)
这是我在水平4.2 中针对水平滚动找到的最简单的方法:
我正在使用 visibleCells
上的第一个单元格,然后滚动到该位置,如果第一个可见单元格显示的宽度小于其一半的宽度,那么我将滚动到下一个单元格
如果您的收藏集垂直滚动,只需将x
更改为y
,将width
更改为height
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee = scrollView.contentOffset
var indexes = self.collectionView.indexPathsForVisibleItems
indexes.sort()
var index = indexes.first!
let cell = self.collectionView.cellForItem(at: index)!
let position = self.collectionView.contentOffset.x - cell.frame.origin.x
if position > cell.frame.size.width/2{
index.row = index.row+1
}
self.collectionView.scrollToItem(at: index, at: .left, animated: true )
}
答案 9 :(得分:3)
有点像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);
}
}
}
答案 10 :(得分:2)
这是我在垂直基于单元格的分页的 Swift 5 中的实现:
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
// Page height used for estimating and calculating paging.
let pageHeight = self.itemSize.height + self.minimumLineSpacing
// Make an estimation of the current page position.
let approximatePage = self.collectionView!.contentOffset.y/pageHeight
// Determine the current page based on velocity.
let currentPage = (velocity.y < 0.0) ? floor(approximatePage) : ceil(approximatePage)
// Create custom flickVelocity.
let flickVelocity = velocity.y * 0.3
// Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)
let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - self.collectionView!.contentInset.top
return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset)
}
一些说明:
itemSize
是否与项目的大小相符,因为这通常是个问题。self.collectionView.decelerationRate = UIScollViewDecelerationRateFast
。这是横向版本(未经过彻底测试,请原谅任何错误):
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
// Page width used for estimating and calculating paging.
let pageWidth = self.itemSize.width + self.minimumLineSpacing
// Make an estimation of the current page position.
let approximatePage = self.collectionView!.contentOffset.x/pageWidth
// Determine the current page based on velocity.
let currentPage = (velocity.x < 0.0) ? floor(approximatePage) : ceil(approximatePage)
// Create custom flickVelocity.
let flickVelocity = velocity.x * 0.3
// Check how many pages the user flicked, if <= 1 then flickedPages should return 0.
let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)
// Calculate newHorizontalOffset.
let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - self.collectionView!.contentInset.left
return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y)
}
答案 11 :(得分:2)
快捷键5
我找到了一种无需子类化UICollectionView即可做到的方法,只需水平计算contentOffset即可。显然,没有将isPagingEnabled设置为true。 这是代码:
var offsetScroll1 : CGFloat = 0
var offsetScroll2 : CGFloat = 0
let flowLayout = UICollectionViewFlowLayout()
let screenSize : CGSize = UIScreen.main.bounds.size
var items = ["1", "2", "3", "4", "5"]
override func viewDidLoad() {
super.viewDidLoad()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 7
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 590, width: screenSize.width, height: 200), collectionViewLayout: flowLayout)
collectionView.register(collectionViewCell1.self, forCellWithReuseIdentifier: cellReuseIdentifier)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.clear
collectionView.showsHorizontalScrollIndicator = false
self.view.addSubview(collectionView)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
offsetScroll1 = offsetScroll2
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
offsetScroll1 = offsetScroll2
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>){
let indexOfMajorCell = self.desiredIndex()
let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
flowLayout.collectionView!.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
targetContentOffset.pointee = scrollView.contentOffset
}
private func desiredIndex() -> Int {
var integerIndex = 0
print(flowLayout.collectionView!.contentOffset.x)
offsetScroll2 = flowLayout.collectionView!.contentOffset.x
if offsetScroll2 > offsetScroll1 {
integerIndex += 1
let offset = flowLayout.collectionView!.contentOffset.x / screenSize.width
integerIndex = Int(round(offset))
if integerIndex < (items.count - 1) {
integerIndex += 1
}
}
if offsetScroll2 < offsetScroll1 {
let offset = flowLayout.collectionView!.contentOffset.x / screenSize.width
integerIndex = Int(offset.rounded(.towardZero))
}
let targetIndex = integerIndex
return targetIndex
}
答案 12 :(得分:1)
您还可以创建假滚动视图来处理滚动。
水平或垂直
// === 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)
}
答案 13 :(得分:0)
stmt := q'!insert into MY_TBL (Col) values('ER0002')!';
答案 14 :(得分:0)
这是Swift5中的优化解决方案,包括处理错误的indexPath。-Michael Lin Liu
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee = scrollView.contentOffset
//M: Get the first visiable item's indexPath from visibaleItems.
var indexPaths = *YOURCOLLECTIONVIEW*.indexPathsForVisibleItems
indexPaths.sort()
var indexPath = indexPaths.first!
//M: Use the velocity to detect the paging control movement.
//M: If the movement is forward, then increase the indexPath.
if velocity.x > 0{
indexPath.row += 1
//M: If the movement is in the next section, which means the indexPath's row is out range. We set the indexPath to the first row of the next section.
if indexPath.row == *YOURCOLLECTIONVIEW*.numberOfItems(inSection: indexPath.section){
indexPath.row = 0
indexPath.section += 1
}
}
else{
//M: If the movement is backward, the indexPath will be automatically changed to the first visiable item which is indexPath.row - 1. So there is no need to write the logic.
}
//M: Tell the collection view to scroll to the next item.
*YOURCOLLECTIONVIEW*.scrollToItem(at: indexPath, at: .left, animated: true )
}
答案 15 :(得分:0)
ОленьБезрогий的原始答案有问题,因此在最后一个单元格收集视图上滚动到开头
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee = scrollView.contentOffset
var indexes = yourCollectionView.indexPathsForVisibleItems
indexes.sort()
var index = indexes.first!
// if velocity.x > 0 && (Get the number of items from your data) > index.row + 1 {
if velocity.x > 0 && yourCollectionView.numberOfItems(inSection: 0) > index.row + 1 {
index.row += 1
} else if velocity.x == 0 {
let cell = yourCollectionView.cellForItem(at: index)!
let position = yourCollectionView.contentOffset.x - cell.frame.origin.x
if position > cell.frame.size.width / 2 {
index.row += 1
}
}
yourCollectionView.scrollToItem(at: index, at: .centeredHorizontally, animated: true )
}
答案 16 :(得分:0)
final class PagingFlowLayout: UICollectionViewFlowLayout {
private var currentIndex = 0
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let count = collectionView!.numberOfItems(inSection: 0)
let currentAttribute = layoutAttributesForItem(
at: IndexPath(item: currentIndex, section: 0)
) ?? UICollectionViewLayoutAttributes()
let direction = proposedContentOffset.x > currentAttribute.frame.minX
if collectionView!.contentOffset.x + collectionView!.bounds.width < collectionView!.contentSize.width || currentIndex < count - 1 {
currentIndex += direction ? 1 : -1
currentIndex = max(min(currentIndex, count - 1), 0)
}
let indexPath = IndexPath(item: currentIndex, section: 0)
let closestAttribute = layoutAttributesForItem(at: indexPath) ?? UICollectionViewLayoutAttributes()
let centerOffset = collectionView!.bounds.size.width / 2
return CGPoint(x: closestAttribute.center.x - centerOffset, y: 0)
}
}
答案 17 :(得分:0)
我创建了一个自定义集合视图布局here,它支持:
就这么简单:
let layout = PagingCollectionViewLayout()
layout.itemSize =
layout.minimumLineSpacing =
layout.scrollDirection =
您只需将PagingCollectionViewLayout.swift添加到您的项目中
或
将pod 'PagingCollectionViewLayout'
添加到您的podfile
答案 18 :(得分:0)
这是我的解决方案,希望在Swift 4.2中为您提供帮助。
class SomeViewController: UIViewController {
private lazy var flowLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: /* width */, height: /* height */)
layout.minimumLineSpacing = // margin
layout.minimumInteritemSpacing = 0.0
layout.sectionInset = UIEdgeInsets(top: 0.0, left: /* margin */, bottom: 0.0, right: /* margin */)
layout.scrollDirection = .horizontal
return layout
}()
private lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView.showsHorizontalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
// collectionView.register(SomeCell.self)
return collectionView
}()
private var currentIndex: Int = 0
}
// MARK: - UIScrollViewDelegate
extension SomeViewController {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
guard scrollView == collectionView else { return }
let pageWidth = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
currentIndex = Int(scrollView.contentOffset.x / pageWidth)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
guard scrollView == collectionView else { return }
let pageWidth = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
var targetIndex = Int(roundf(Float(targetContentOffset.pointee.x / pageWidth)))
if targetIndex > currentIndex {
targetIndex = currentIndex + 1
} else if targetIndex < currentIndex {
targetIndex = currentIndex - 1
}
let count = collectionView.numberOfItems(inSection: 0)
targetIndex = max(min(targetIndex, count - 1), 0)
print("targetIndex: \(targetIndex)")
targetContentOffset.pointee = scrollView.contentOffset
var offsetX: CGFloat = 0.0
if targetIndex < count - 1 {
offsetX = pageWidth * CGFloat(targetIndex)
} else {
offsetX = scrollView.contentSize.width - scrollView.width
}
collectionView.setContentOffset(CGPoint(x: offsetX, y: 0.0), animated: true)
}
}
答案 19 :(得分:0)
您可以使用以下库:https://github.com/ink-spot/UPCarouselFlowLayout
这非常简单,而且您无需像其他答案所包含的那样考虑细节。
答案 20 :(得分:0)
好的,所以建议的答案对我不起作用,因为我想按部分滚动,因此页面宽度可变
我这样做了(仅垂直):
var pagesSizes = [CGSize]()
func scrollViewDidScroll(_ scrollView: UIScrollView) {
defer {
lastOffsetY = scrollView.contentOffset.y
}
if collectionView.isDecelerating {
var currentPage = 0
var currentPageBottom = CGFloat(0)
for pagesSize in pagesSizes {
currentPageBottom += pagesSize.height
if currentPageBottom > collectionView!.contentOffset.y {
break
}
currentPage += 1
}
if collectionView.contentOffset.y > currentPageBottom - pagesSizes[currentPage].height, collectionView.contentOffset.y + collectionView.frame.height < currentPageBottom {
return // 100% of view within bounds
}
if lastOffsetY < collectionView.contentOffset.y {
if currentPage + 1 != pagesSizes.count {
collectionView.setContentOffset(CGPoint(x: 0, y: currentPageBottom), animated: true)
}
} else {
collectionView.setContentOffset(CGPoint(x: 0, y: currentPageBottom - pagesSizes[currentPage].height), animated: true)
}
}
}
在这种情况下,我预先使用部分高度+页眉+页脚计算每个页面大小,并将其存储在数组中。那就是pagesSizes
成员
答案 21 :(得分: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)
}
答案 22 :(得分:-1)
这是我使用UICollectionViewFlowLayout
覆盖targetContentOffset
的方法:
(尽管最后,我最终没有使用它,而是使用UIPageViewController。)
/**
A UICollectionViewFlowLayout with...
- paged horizontal scrolling
- itemSize is the same as the collectionView bounds.size
*/
class PagedFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
self.scrollDirection = .horizontal
self.minimumLineSpacing = 8 // line spacing is the horizontal spacing in horizontal scrollDirection
self.minimumInteritemSpacing = 0
if #available(iOS 11.0, *) {
self.sectionInsetReference = .fromSafeArea // for iPhone X
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("not implemented")
}
// Note: Setting `minimumInteritemSpacing` here will be too late. Don't do it here.
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
collectionView.decelerationRate = UIScrollViewDecelerationRateFast // mostly you want it fast!
let insetedBounds = UIEdgeInsetsInsetRect(collectionView.bounds, self.sectionInset)
self.itemSize = insetedBounds.size
}
// Table: Possible cases of targetContentOffset calculation
// -------------------------
// start | |
// near | velocity | end
// page | | page
// -------------------------
// 0 | forward | 1
// 0 | still | 0
// 0 | backward | 0
// 1 | forward | 1
// 1 | still | 1
// 1 | backward | 0
// -------------------------
override func targetContentOffset( //swiftlint:disable:this cyclomatic_complexity
forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return proposedContentOffset }
let pageWidth = itemSize.width + minimumLineSpacing
let currentPage: CGFloat = collectionView.contentOffset.x / pageWidth
let nearestPage: CGFloat = round(currentPage)
let isNearPreviousPage = nearestPage < currentPage
var pageDiff: CGFloat = 0
let velocityThreshold: CGFloat = 0.5 // can customize this threshold
if isNearPreviousPage {
if velocity.x > velocityThreshold {
pageDiff = 1
}
} else {
if velocity.x < -velocityThreshold {
pageDiff = -1
}
}
let x = (nearestPage + pageDiff) * pageWidth
let cappedX = max(0, x) // cap to avoid targeting beyond content
//print("x:", x, "velocity:", velocity)
return CGPoint(x: cappedX, y: proposedContentOffset.y)
}
}