UICollectionView 列的垂直偏移
Vertical offset on a UICollectionView column
右图是我努力实现的
有谁知道我如何在两列 UICollectionView 上实现这一点?
我可以通过测试 if (indexPath.row % 2 = 0) 来辨别我的专栏,但我无法设法实现这个结果。
这是一种简单的方法吗?或者我需要自定义 UICollectionViewFlowLayout 吗?
注意:我所有的单元格大小都一样。
是的,您需要创建自定义 UICollectionViewLayout
,这很容易实现。您只需要以下内容:
prepare():执行提供布局信息所需的前期计算
collectionViewContentSize: Return 整个内容区域的总体大小基于您的初步计算
layoutAttributesForElements(in:): Return 指定矩形中的单元格和视图的属性
既然我们有相同大小的单元格,就很容易实现了。
让我们声明几个要使用的变量:
// Adjust this as you need
fileprivate var offset: CGFloat = 50
// Properties for configuring the layout: the number of columns and the cell padding.
fileprivate var numberOfColumns = 2
fileprivate var cellPadding: CGFloat = 10
// Cache the calculated attributes. When you call prepare(), you’ll calculate the attributes for all items and add them to the cache. You can be efficient and
fileprivate var cache = [UICollectionViewLayoutAttributes]()
// Properties to store the content size.
fileprivate var contentHeight: CGFloat = 0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
接下来让我们覆盖 prepare()
方法
override func prepare() {
// If cache is empty and the collection view exists – calculate the layout attributes
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
// xOffset: array with the x-coordinate for every column based on the column widths
// yOffset: array with the y-position for every column, Using odd-even logic to push the even cell upwards and odd cells down.
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset = [CGFloat]()
for i in 0..<numberOfColumns {
yOffset.append((i % 2 == 0) ? 0 : offset)
}
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// Calculate insetFrame that can be set to the attribute
let cellHeight = columnWidth - (cellPadding * 2)
let height = cellPadding * 2 + cellHeight
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// Create an instance of UICollectionViewLayoutAttribute, sets its frame using insetFrame and appends the attributes to cache.
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// Update the contentHeight to account for the frame of the newly calculated item. It then advances the yOffset for the current column based on the frame
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
最后我们需要 collectionViewContentSize
和 layoutAttributesForElements(in:)
// Using contentWidth and contentHeight from previous steps, calculate collectionViewContentSize.
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
您可以通过 class.
找到 gist
它是这样显示的:
注意:这只是一个演示,您可能需要根据需要进行调整。
希望对您有所帮助!
右图是我努力实现的
这是一种简单的方法吗?或者我需要自定义 UICollectionViewFlowLayout 吗?
注意:我所有的单元格大小都一样。
是的,您需要创建自定义 UICollectionViewLayout
,这很容易实现。您只需要以下内容:
prepare():执行提供布局信息所需的前期计算
collectionViewContentSize: Return 整个内容区域的总体大小基于您的初步计算
layoutAttributesForElements(in:): Return 指定矩形中的单元格和视图的属性
既然我们有相同大小的单元格,就很容易实现了。
让我们声明几个要使用的变量:
// Adjust this as you need
fileprivate var offset: CGFloat = 50
// Properties for configuring the layout: the number of columns and the cell padding.
fileprivate var numberOfColumns = 2
fileprivate var cellPadding: CGFloat = 10
// Cache the calculated attributes. When you call prepare(), you’ll calculate the attributes for all items and add them to the cache. You can be efficient and
fileprivate var cache = [UICollectionViewLayoutAttributes]()
// Properties to store the content size.
fileprivate var contentHeight: CGFloat = 0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
接下来让我们覆盖 prepare()
方法
override func prepare() {
// If cache is empty and the collection view exists – calculate the layout attributes
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
// xOffset: array with the x-coordinate for every column based on the column widths
// yOffset: array with the y-position for every column, Using odd-even logic to push the even cell upwards and odd cells down.
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset = [CGFloat]()
for i in 0..<numberOfColumns {
yOffset.append((i % 2 == 0) ? 0 : offset)
}
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// Calculate insetFrame that can be set to the attribute
let cellHeight = columnWidth - (cellPadding * 2)
let height = cellPadding * 2 + cellHeight
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// Create an instance of UICollectionViewLayoutAttribute, sets its frame using insetFrame and appends the attributes to cache.
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// Update the contentHeight to account for the frame of the newly calculated item. It then advances the yOffset for the current column based on the frame
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
最后我们需要 collectionViewContentSize
和 layoutAttributesForElements(in:)
// Using contentWidth and contentHeight from previous steps, calculate collectionViewContentSize.
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
您可以通过 class.
找到 gist它是这样显示的:
注意:这只是一个演示,您可能需要根据需要进行调整。
希望对您有所帮助!