Tru*_*an1 11 ios uicollectionview uicollectionviewlayout swift
我想在多行App Store集合视图中复制分页:
到目前为止,我已经设计了尽可能接近它的外观,包括显示前一个和下一个单元格,但不知道如何使分页工作,因此它捕捉下一组3:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.collectionViewLayout = MultiRowLayout(
rowsCount: 3,
inset: 16
)
}
...
class MultiRowLayout: UICollectionViewFlowLayout {
private var rowsCount: CGFloat = 0
convenience init(rowsCount: CGFloat, spacing: CGFloat? = nil, inset: CGFloat? = nil) {
self.init()
self.scrollDirection = .horizontal
self.minimumInteritemSpacing = 0
self.rowsCount = rowsCount
if let spacing = spacing {
self.minimumLineSpacing = spacing
}
if let inset = inset {
self.sectionInset = UIEdgeInsets(top: 0, left: inset, bottom: 0, right: inset)
}
}
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
self.itemSize = calculateItemSize(from: collectionView.bounds.size)
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
guard let collectionView = collectionView,
!newBounds.size.equalTo(collectionView.bounds.size) else {
return false
}
itemSize = calculateItemSize(from: collectionView.bounds.size)
return true
}
}
private extension MultiRowLayout {
func calculateItemSize(from bounds: CGSize) -> CGSize {
return CGSize(
width: bounds.width - minimumLineSpacing * 2 - sectionInset.left,
height: bounds.height / rowsCount
)
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,只有当单元格是集合视图的100%宽度时,本机isPagingEnabled标志UICollectionView才有效,因此用户不会看到前一个和下一个单元格.
我有一个工作快照分页功能,但每页只有一个项目,而不是这3行的集合.有人可以帮助使分组行的快照分页工作,而不是每页一个项目吗?
rob*_*off 18
没有理由UICollectionViewFlowLayout仅为此行为进行子类化.
UICollectionView是它的子类UIScrollView,因此它的委托协议UICollectionViewDelegate是一个子类型UIScrollViewDelegate.这意味着您可以UIScrollViewDelegate在集合视图的委托中实现任何方法.
在集合视图的委托中,实现scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)将目标内容偏移量舍入到最近的单元格列的左上角.
这是一个示例实现:
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let layout = collectionViewLayout as! UICollectionViewFlowLayout
let bounds = scrollView.bounds
let xTarget = targetContentOffset.pointee.x
// This is the max contentOffset.x to allow. With this as contentOffset.x, the right edge of the last column of cells is at the right edge of the collection view's frame.
let xMax = scrollView.contentSize.width - scrollView.bounds.width
if abs(velocity.x) <= snapToMostVisibleColumnVelocityThreshold {
let xCenter = scrollView.bounds.midX
let poses = layout.layoutAttributesForElements(in: bounds) ?? []
// Find the column whose center is closest to the collection view's visible rect's center.
let x = poses.min(by: { abs($0.center.x - xCenter) < abs($1.center.x - xCenter) })?.frame.origin.x ?? 0
targetContentOffset.pointee.x = x
} else if velocity.x > 0 {
let poses = layout.layoutAttributesForElements(in: CGRect(x: xTarget, y: 0, width: bounds.size.width, height: bounds.size.height)) ?? []
// Find the leftmost column beyond the current position.
let xCurrent = scrollView.contentOffset.x
let x = poses.filter({ $0.frame.origin.x > xCurrent}).min(by: { $0.center.x < $1.center.x })?.frame.origin.x ?? xMax
targetContentOffset.pointee.x = min(x, xMax)
} else {
let poses = layout.layoutAttributesForElements(in: CGRect(x: xTarget - bounds.size.width, y: 0, width: bounds.size.width, height: bounds.size.height)) ?? []
// Find the rightmost column.
let x = poses.max(by: { $0.center.x < $1.center.x })?.frame.origin.x ?? 0
targetContentOffset.pointee.x = max(x, 0)
}
}
// Velocity is measured in points per millisecond.
private var snapToMostVisibleColumnVelocityThreshold: CGFloat { return 0.3 }
Run Code Online (Sandbox Code Playgroud)
结果:
您可以在此处找到我的测试项目的完整源代码:https://github.com/mayoff/multiRowSnapper
Tia*_*ida 10
有了 iOS 13,这变得容易多了!
在 iOS 13 中,您可以使用UICollectionViewCompositionalLayout.
这引入了几个概念,我将尝试在这里给出一个要点,但我认为理解这一点很值得!
在 CompositionalLayout 中,您有 3 个实体可以指定大小。您可以使用绝对值、分数值(例如一半)或估计值来指定大小。这3个实体是:
NSCollectionLayoutItem)你的细胞大小。分数大小与项目所在的组有关,它们可以考虑父项的宽度或高度。
NSCollectionLayoutGroup)组允许您创建一组项目。在那个应用商店示例中,一个组是一个列并且有 3 个项目,因此您的项目应该从该组中获得 0.33 的高度。然后,例如,您可以说该组需要 300 高度。
NSCollectionLayoutSection)部分声明了该组将如何重复。在这种情况下,您可以说该部分将是水平的。
您可以使用接收节索引和NSCollectionLayoutEnvironment. 这很有用,因为您可以为每个特征设置不同的布局(例如,在 iPad 上您可以有不同的内容)和每个部分的索引(即,您可以有 1 个具有水平滚动的部分,另一个只是垂直布局)。
func createCollectionViewLayout() {
let layout = UICollectionViewCompositionalLayout { sectionIndex, _ in
return self.createAppsColumnsLayout()
}
let config = UICollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .vertical
config.interSectionSpacing = 31
layout.configuration = config
return layout
}
Run Code Online (Sandbox Code Playgroud)
在应用商店的例子中,你有一个Paul Hudson的非常好的视频,来自Hacking with Swift解释了这一点。他也有这个回购!
但是,我会将代码放在这里,以免丢失:
func createAppsColumnsLayout(using section: Section) -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(0.33)
)
let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)
layoutItem.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: 5,
bottom: 0,
trailing: 5
)
let layoutGroupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.93),
heightDimension: .fractionalWidth(0.55)
)
let layoutGroup = NSCollectionLayoutGroup.vertical(
layoutSize: layoutGroupSize,
subitems: [layoutItem]
)
let layoutSection = NSCollectionLayoutSection(group: layoutGroup)
layoutSection.orthogonalScrollingBehavior = .groupPagingCentered
return layoutSection
}
Run Code Online (Sandbox Code Playgroud)
最后,您只需要设置布局:
collectionView.collectionViewLayout = createCompositionalLayout()
Run Code Online (Sandbox Code Playgroud)
附带的一件很酷的事情UICollectionViewCompositionalLayout是不同的页面机制,例如groupPagingCentered,但我认为这个答案已经足够长,可以解释它们之间的区别
| 归档时间: |
|
| 查看次数: |
1871 次 |
| 最近记录: |