ios - uicollectionview objective c
按單元分頁UICollectionView,而不是屏幕 (8)
方法1:收集視圖
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)
}
}
方法2:頁面視圖控制器
如果符合您的要求,您可以使用UIPageViewController
,每個頁面都有獨立的視圖控制器。
我有UICollectionView
水平滾動,並且每個整個屏幕總是有2個單元格並排。 我需要滾動停止在一個單元格的開始。 啟用分頁功能後,集合視圖會一次滾動整個頁面,即2個單元格,然後停止。
我需要啟用單個單元格的滾動,或者停止在單元格邊緣的多個單元格滾動。
我試圖UICollectionViewFlowLayout
並實現方法targetContentOffsetForProposedContentOffset
,但到目前為止我只能打破我的集合視圖,並停止滾動。 有沒有更簡單的方法來實現這一點,或者我是否真的需要實現UICollectionViewFlowLayout
子類的所有方法? 謝謝。
使用自定義頁面寬度進行水平分頁(Swift 4)
這裡提出的許多解決方案導致一些奇怪的行為,感覺不像正確實施分頁。
但是, 本教程中介紹的解決方案似乎沒有任何問題。 它只是一個完美的分頁算法。 您可以通過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
設置相同的值(您也可以將此值存儲在中心位置)並設置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)
}