我为你写了开始布局。你应该验证我发布的所有代码。使用如下自定义布局:
let layout = CustomLayout()
layout.minimumLineSpacing = 4
layout.minimumInteritemSpacing = 4
collectionView.collectionViewLayout = layout
collectionView.backgroundColor = .lightGray
...
// Delegate
func collectionView(_ collectionView: UICollectionView, sizeForPhotoAtIndexPath indexPath: IndexPath) -> CGSize {
return images[indexPath.row].size
}
代码
自定义布局
.
import UIKit
protocol CustomLayoutDelegate: class {
func collectionView(_ collectionView: UICollectionView, sizeForPhotoAtIndexPath indexPath: IndexPath) -> CGSize
}
class CustomLayout: UICollectionViewFlowLayout {
var preferredHeight: CGFloat = 100 {
didSet {
invalidateLayout()
}
}
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentSize: CGSize = .zero
override func prepare() {
super.prepare()
cache.removeAll()
guard let collectionView = collectionView,
let delegate = collectionView.delegate as? CustomLayoutDelegate else {
return
}
var sizes: [IndexPath: CGSize] = [:]
let maxRowWidth = collectionView.frame.width - (collectionView.contentInset.left + collectionView.contentInset.right)
var offsetY: CGFloat = 0
var rowIndexes: [IndexPath] = []
var rowWidth: CGFloat = 0
let spacing = minimumInteritemSpacing
let numberOfItems = collectionView.numberOfItems(inSection: 0)
for item in 0..<numberOfItems {
let indexPath = IndexPath(item: item, section: 0)
let size = delegate.collectionView(collectionView, sizeForPhotoAtIndexPath: indexPath)
sizes[indexPath] = size
let aspectRatio = size.width / size.height
let preferredWidth = preferredHeight * aspectRatio
rowWidth += preferredWidth
rowIndexes.append(indexPath)
if rowIndexes.count > 1 {
// Check if we fit row width.
let rowWidthWithSpacing = rowWidth + CGFloat(rowIndexes.count - 1) * spacing
if rowWidthWithSpacing > maxRowWidth {
let previousRowWidthWithSpacing = rowWidthWithSpacing - spacing - preferredWidth
let diff = abs(maxRowWidth - rowWidthWithSpacing)
let previousDiff = abs(maxRowWidth - previousRowWidthWithSpacing)
let scale: CGFloat
let finalRowIndexPaths: [IndexPath]
if previousDiff < diff {
rowWidth -= preferredWidth
rowIndexes.removeLast()
finalRowIndexPaths = rowIndexes
scale = maxRowWidth / rowWidth
rowWidth = preferredWidth
rowIndexes = [indexPath]
} else {
finalRowIndexPaths = rowIndexes
scale = maxRowWidth / rowWidth
rowWidth = 0
rowIndexes = []
}
let finalHeight = preferredHeight * scale
var offsetX: CGFloat = 0
finalRowIndexPaths.forEach {
let size = sizes[$0]!
let scale = finalHeight / size.height
let attributes = UICollectionViewLayoutAttributes(forCellWith: $0)
attributes.frame = CGRect(x: offsetX, y: offsetY, width: size.width * scale, height: size.height * scale).integral
offsetX = attributes.frame.maxX + spacing
cache.append(attributes)
}
offsetY = (cache.last?.frame.maxY ?? 0) + minimumLineSpacing
}
}
if numberOfItems == item + 1 && !rowIndexes.isEmpty {
let finalHeight = preferredHeight
var offsetX: CGFloat = 0
rowIndexes.forEach {
let size = sizes[$0]!
let scale = finalHeight / size.height
let attributes = UICollectionViewLayoutAttributes(forCellWith: $0)
attributes.frame = CGRect(x: offsetX, y: offsetY, width: size.width * scale, height: size.height * scale).integral
offsetX = attributes.frame.maxX + spacing
cache.append(attributes)
}
offsetY = (cache.last?.frame.maxY ?? 0) + minimumLineSpacing
}
contentSize = CGSize(width: collectionView.frame.width, height: cache.last?.frame.maxY ?? 0)
}
}
override var collectionViewContentSize: CGSize {
return contentSize
}
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]
}
}