我有两个单元格尺寸较小(间距和插图前大约半屏宽度)和较大(插图前的全屏宽度),这两种尺寸可以在下图中看到。
我希望UICollectionView根据给定的大小属性自动调整这些单元格的大小。我已经设法正确调整了单元格的大小,但是当顺序中没有两个小单元格时,我无法正确布局单元格。
当数组中有两个小单元格顺序时会发生这种情况:
这是这个单元格应该去的地方:
这是我自己制作的自定义UICollectionViewLayout的准备函数:
import UIKit
protocol FlexLayoutDelegate: class {
func collectionView(_ collectionView:UICollectionView, sizeForViewAtIndexPath indexPath:IndexPath) -> Int
}
class FlexLayout: UICollectionViewLayout {
weak var delegate: FlexLayoutDelegate!
fileprivate var cellPadding: CGFloat = 10
fileprivate var cache = [UICollectionViewLayoutAttributes]()
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)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// Check if cache is empty
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
var yOffset = CGFloat(0)
var xOffset = CGFloat(0)
var column = 0
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let viewSize: CGFloat = CGFloat(delegate.collectionView(collectionView, sizeForViewAtIndexPath: indexPath))
let height = cellPadding * 2 + 240
let width = contentWidth / viewSize
if viewSize == 2 {
xOffset = CGFloat(column) * width
} else {
xOffset = 0
}
print(width)
let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
column = column < 1 ? column + 1 : 0
yOffset = column == 0 || getNextCellSize(currentCell: indexPath.row, collectionView: collectionView) == 1 ? yOffset + height : yOffset
}
}
func getNextCellSize(currentCell: Int, collectionView: UICollectionView) -> Int {
var nextViewSize = 0
if currentCell < (collectionView.numberOfItems(inSection: 0) - 1) {
nextViewSize = delegate.collectionView(collectionView, sizeForViewAtIndexPath: IndexPath(item: currentCell + 1, section: 0))
}
return nextViewSize
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
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]
}
}
由于
答案 0 :(得分:1)
您需要添加一个数组来跟踪列的高度,并查看最短的列是private var columsHeights : [CGFloat] = []
,还需要一个(Int,Float)
元组数组来保留可填充的空格,我还在委托中添加了一个方法来获取集合视图中我们想要的列数,以及一个方法来了解是否可以根据大小添加单元格。
然后,如果我们要添加一个单元格,我们检查是否可以添加,因为第一列已填充,我们在avaiableSpaces
数组中添加对应于column2的空格,当我们首先添加下一个单元格时检查是否可以添加到任何可用空间中,如果可以添加,我们添加和删除可用空间。
以下是代码
FlexLayoutDelegate实施
extension ViewController: FlexLayoutDelegate{
func collectionView(_ collectionView:UICollectionView,sizeForViewAtIndexPath indexPath:IndexPath) ->Int{
if(indexPath.row % 2 == 1)
{
return 1
}
return 2
}
func numberOfColumnsInCollectionView(collectionView:UICollectionView) ->Int{
return 2
}
}
<强> FlexLayout 强>
import UIKit
protocol FlexLayoutDelegate: class {
func collectionView(_ collectionView:UICollectionView, sizeForViewAtIndexPath indexPath:IndexPath) -> Int
// Returns the amount of columns that have to display at that moment
func numberOfColumnsInCollectionView(collectionView:UICollectionView) ->Int
}
class FlexLayout: UICollectionViewLayout {
weak var delegate: FlexLayoutDelegate!
fileprivate var cellPadding: CGFloat = 10
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentHeight: CGFloat = 0
private var columsHeights : [CGFloat] = []
private var avaiableSpaces : [(Int,CGFloat)] = []
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
var columnsQuantity : Int{
get{
if(self.delegate != nil)
{
return (self.delegate?.numberOfColumnsInCollectionView(collectionView: self.collectionView!))!
}
return 0
}
}
//MARK: PRIVATE METHODS
private func shortestColumnIndex() -> Int{
var retVal : Int = 0
var shortestValue = MAXFLOAT
var i = 0
for columnHeight in columsHeights {
//debugPrint("Column Height: \(columnHeight) index: \(i)")
if(Float(columnHeight) < shortestValue)
{
shortestValue = Float(columnHeight)
retVal = i
}
i += 1
}
//debugPrint("shortest Column index: \(retVal)")
return retVal
}
private func canUseDoubleColumnOnIndex(columnIndex:Int) ->Bool
{
var retVal = false
if(columnIndex < self.columnsQuantity-1)
{
let firstColumnHeight = columsHeights[columnIndex]
let secondColumnHeight = columsHeights[columnIndex + 1]
//debugPrint(firstColumnHeight - secondColumnHeight)
retVal = firstColumnHeight == secondColumnHeight
}
return retVal
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// Check if cache is empty
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
// Set all column heights to 0
self.columsHeights = []
for _ in 0..<self.columnsQuantity {
self.columsHeights.append(0)
}
var xOffset = CGFloat(0)
var column = 0
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let viewSize: CGFloat = CGFloat(delegate.collectionView(collectionView, sizeForViewAtIndexPath: indexPath))
let height = cellPadding * 2 + 240
let width = contentWidth / viewSize
if viewSize == 2 {
xOffset = CGFloat(column) * width
} else {
xOffset = 0
}
var columIndex = self.shortestColumnIndex()
var yOffset = self.columsHeights[columIndex]
if(viewSize == 1){//Double Cell
if(self.canUseDoubleColumnOnIndex(columnIndex: columIndex)){
// Set column height
self.columsHeights[columIndex] = CGFloat(yOffset) + height
self.columsHeights[columIndex + 1] = CGFloat(yOffset) + height
}else{
self.avaiableSpaces.append((columIndex,yOffset))
// Set column height
yOffset += height
xOffset = 0
columIndex = 0
self.columsHeights[columIndex] = CGFloat(yOffset) + height
self.columsHeights[columIndex + 1] = CGFloat(yOffset) + height
}
}else{
//if there is not avaiable space
if(self.avaiableSpaces.count == 0)
{
// Set column height
self.columsHeights[columIndex] = CGFloat(yOffset) + height
}else{//if there is some avaiable space
yOffset = self.avaiableSpaces.first!.1
xOffset = CGFloat(self.avaiableSpaces.first!.0) * width
self.avaiableSpaces.remove(at: 0)
}
}
print(width)
let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
column = column < 1 ? column + 1 : 0
yOffset = column == 0 || getNextCellSize(currentCell: indexPath.row, collectionView: collectionView) == 1 ? yOffset + height : yOffset
}
}
func getNextCellSize(currentCell: Int, collectionView: UICollectionView) -> Int {
var nextViewSize = 0
if currentCell < (collectionView.numberOfItems(inSection: 0) - 1) {
nextViewSize = delegate.collectionView(collectionView, sizeForViewAtIndexPath: IndexPath(item: currentCell + 1, section: 0))
}
return nextViewSize
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
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]
}
}
<强>结果强>