Mar*_*rke 53 objective-c uiscrollview ios uicollectionview
我知道有些人之前已经问过这个问题,但他们都是关于UITableViews
或者UIScrollViews
我无法获得为我工作的公认解决方案.我想要的是在UICollectionView
水平滚动时的捕捉效果- 就像iOS AppStore中发生的那样.iOS 9+是我的目标版本,因此请在回答之前先查看UIKit的更改.
谢谢.
Mar*_*rke 93
虽然最初我使用的是Objective-C,但是我自从切换到了Swift并且原来接受的答案还不够.
我最终创建了一个UICollectionViewLayout
子类,它提供了最好的(imo)体验,而不是在用户停止滚动时改变内容偏移或类似内容的其他函数.
class SnappingCollectionViewLayout: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }
var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left
let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)
let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)
layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemOffset = layoutAttributes.frame.origin.x
if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustment)) {
offsetAdjustment = itemOffset - horizontalOffset
}
})
return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
}
}
Run Code Online (Sandbox Code Playgroud)
对于当前布局子类的最原始感觉减速,请确保设置以下内容:
collectionView?.decelerationRate = UIScrollViewDecelerationRateFast
Nam*_*Nam 24
这里有什么值得我使用的简单计算(在swift中):
func snapToNearestCell(_ collectionView: UICollectionView) {
for i in 0..<collectionView.numberOfItems(inSection: 0) {
let itemWithSpaceWidth = collectionViewFlowLayout.itemSize.width + collectionViewFlowLayout.minimumLineSpacing
let itemWidth = collectionViewFlowLayout.itemSize.width
if collectionView.contentOffset.x <= CGFloat(i) * itemWithSpaceWidth + itemWidth / 2 {
let indexPath = IndexPath(item: i, section: 0)
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
break
}
}
}
Run Code Online (Sandbox Code Playgroud)
打电话到你需要的地方.我打电话给它
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
snapToNearestCell(scrollView)
}
Run Code Online (Sandbox Code Playgroud)
和
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
snapToNearestCell(scrollView)
}
Run Code Online (Sandbox Code Playgroud)
collectionViewFlowLayout可能来自:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Set up collection view
collectionViewFlowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
}
Run Code Online (Sandbox Code Playgroud)
Vah*_*iri 22
根据Mete的回答和Chris Chute的评论,
这是一个Swift 4扩展,它将完成OP想要的.它在单行和双行嵌套集合视图上进行了测试,它运行得很好.
extension UICollectionView {
func scrollToNearestVisibleCollectionViewCell() {
self.decelerationRate = UIScrollViewDecelerationRateFast
let visibleCenterPositionOfScrollView = Float(self.contentOffset.x + (self.bounds.size.width / 2))
var closestCellIndex = -1
var closestDistance: Float = .greatestFiniteMagnitude
for i in 0..<self.visibleCells.count {
let cell = self.visibleCells[i]
let cellWidth = cell.bounds.size.width
let cellCenter = Float(cell.frame.origin.x + cellWidth / 2)
// Now calculate closest cell
let distance: Float = fabsf(visibleCenterPositionOfScrollView - cellCenter)
if distance < closestDistance {
closestDistance = distance
closestCellIndex = self.indexPath(for: cell)!.row
}
}
if closestCellIndex != -1 {
self.scrollToItem(at: IndexPath(row: closestCellIndex, section: 0), at: .centeredHorizontally, animated: true)
}
}
}
Run Code Online (Sandbox Code Playgroud)
您需要UIScrollViewDelegate
为集合视图实现协议,然后添加以下两种方法:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.collectionView.scrollToNearestVisibleCollectionViewCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
self.collectionView.scrollToNearestVisibleCollectionViewCell()
}
}
Run Code Online (Sandbox Code Playgroud)
Ric*_*hiy 21
遵循滚动速度,捕捉到最近的单元格。
正常工作。
import UIKit
class SnapCenterLayout: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }
let parent = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
let itemSpace = itemSize.width + minimumInteritemSpacing
var currentItemIdx = round(collectionView.contentOffset.x / itemSpace)
// Skip to the next cell, if there is residual scrolling velocity left.
// This helps to prevent glitches
let vX = velocity.x
if vX > 0 {
currentItemIdx += 1
} else if vX < 0 {
currentItemIdx -= 1
}
let nearestPageOffset = currentItemIdx * itemSpace
return CGPoint(x: nearestPageOffset,
y: parent.y)
}
}
Run Code Online (Sandbox Code Playgroud)
Met*_*tte 18
SWIFT 3版@ Iowa15回复
func scrollToNearestVisibleCollectionViewCell() {
let visibleCenterPositionOfScrollView = Float(collectionView.contentOffset.x + (self.collectionView!.bounds.size.width / 2))
var closestCellIndex = -1
var closestDistance: Float = .greatestFiniteMagnitude
for i in 0..<collectionView.visibleCells.count {
let cell = collectionView.visibleCells[i]
let cellWidth = cell.bounds.size.width
let cellCenter = Float(cell.frame.origin.x + cellWidth / 2)
// Now calculate closest cell
let distance: Float = fabsf(visibleCenterPositionOfScrollView - cellCenter)
if distance < closestDistance {
closestDistance = distance
closestCellIndex = collectionView.indexPath(for: cell)!.row
}
}
if closestCellIndex != -1 {
self.collectionView!.scrollToItem(at: IndexPath(row: closestCellIndex, section: 0), at: .centeredHorizontally, animated: true)
}
}
Run Code Online (Sandbox Code Playgroud)
需要在UIScrollViewDelegate中实现:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollToNearestVisibleCollectionViewCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
scrollToNearestVisibleCollectionViewCell()
}
}
Run Code Online (Sandbox Code Playgroud)
Sou*_*dra 10
func snapToNearestCell(scrollView: UIScrollView) {
let middlePoint = Int(scrollView.contentOffset.x + UIScreen.main.bounds.width / 2)
if let indexPath = self.cvCollectionView.indexPathForItem(at: CGPoint(x: middlePoint, y: 0)) {
self.cvCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
Run Code Online (Sandbox Code Playgroud)
像这样实现滚动视图委托
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
self.snapToNearestCell(scrollView: scrollView)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.snapToNearestCell(scrollView: scrollView)
}
Run Code Online (Sandbox Code Playgroud)
另外,为了更好地捕捉
self.cvCollectionView.decelerationRate = UIScrollViewDecelerationRateFast
Run Code Online (Sandbox Code Playgroud)
奇迹般有效
Nho*_*yen 10
我尝试了@Mark Bourke 和@mrcrowley 解决方案,但它们给出了非常相同的结果,但具有不需要的粘性效果。
我设法通过考虑velocity
. 这是完整的代码。
final class BetterSnappingLayout: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
var offsetAdjusment = CGFloat.greatestFiniteMagnitude
let horizontalCenter = proposedContentOffset.x + (collectionView.bounds.width / 2)
let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)
let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)
layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemHorizontalCenter = layoutAttributes.center.x
if abs(itemHorizontalCenter - horizontalCenter) < abs(offsetAdjusment) {
if abs(velocity.x) < 0.3 { // minimum velocityX to trigger the snapping effect
offsetAdjusment = itemHorizontalCenter - horizontalCenter
} else if velocity.x > 0 {
offsetAdjusment = itemHorizontalCenter - horizontalCenter + layoutAttributes.bounds.width
} else { // velocity.x < 0
offsetAdjusment = itemHorizontalCenter - horizontalCenter - layoutAttributes.bounds.width
}
}
})
return CGPoint(x: proposedContentOffset.x + offsetAdjusment, y: proposedContentOffset.y)
}
Run Code Online (Sandbox Code Playgroud)
}
如果您想要简单的本机行为,而无需自定义:
collectionView.pagingEnabled = YES;
Run Code Online (Sandbox Code Playgroud)
仅当集合视图布局项目的大小全部为一种大小且UICollectionViewCell
的clipToBounds
属性设置为时,此方法才能正常工作YES
。
归档时间: |
|
查看次数: |
30760 次 |
最近记录: |