|
//
// WaterfallFlowLayout.swift
// PaiAi
//
// Created by FFIB on 2017/11/13.
// Copyright © 2017年 yb. All rights reserved.
//
import UIKit
public final class WaterfallFlowLayout: UICollectionViewLayout {
private var minColumn: Int = 0
private var itemWidth: CGFloat = -1
private var columnHeights = [CGFloat]()
private var minColumnHeight: CGFloat = 0
private(set) var configuration = WaterfallFlowConfiguration()
private var attributesArr = [UICollectionViewLayoutAttributes]()
override public init() {
super.init()
}
convenience init(configuration: WaterfallFlowConfiguration) {
self.init()
self.configuration = configuration
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override public func prepare() {
super.prepare()
initialize()
}
override public var collectionViewContentSize: CGSize {
return calculateViewSize()
}
override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard attributesArr.count <= indexPath.row else {
return attributesArr[indexPath.row]
}
let itemX = calculateItemX()
let itemY = calculateItemY()
let itemHeight = calculateItemHeight(indexPath: indexPath)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: itemX, y: itemY, width: itemWidth, height: itemHeight)
setMinColumn(h: itemHeight)
return attributes
}
override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attributesArr
}
fileprivate func initialize() {
guard collectionView?.numberOfSections == 1,
let itemCount = collectionView?.numberOfItems(inSection: 0),
itemCount != 0 else { return }
let originIndex: Int
if attributesArr.count >= itemCount || itemWidth == -1 {
minColumn = 0
originIndex = 0
minColumnHeight = 0
attributesArr.removeAll()
calculateItemWidth()
columnHeights = Array(repeating: 0, count: configuration.columnCount)
} else {
originIndex = attributesArr.count
}
for i in originIndex..<itemCount {
guard let attributes = layoutAttributesForItem(at: IndexPath(row: i, section: 0))
else { continue }
attributesArr.append(attributes)
}
}
fileprivate func calculateViewSize() -> CGSize {
guard let collectionView = collectionView,
let maxH = columnHeights.max() else { return CGSize.zero }
return CGSize(width: collectionView.bounds.width, height: maxH + configuration.rowSpace)
}
fileprivate func calculateItemX() -> CGFloat {
return CGFloat(minColumn) * itemWidth + CGFloat(minColumn + 1) * configuration.columnSpace
}
fileprivate func calculateItemY() -> CGFloat {
minColumnHeight += configuration.rowSpace
return minColumnHeight
}
fileprivate func calculateItemWidth() {
let width = collectionView?.bounds.width ?? 0
itemWidth = (width - configuration.columnSpace * (CGFloat(configuration.columnCount + 1))) / CGFloat(configuration.columnCount)
}
fileprivate func calculateItemHeight(indexPath: IndexPath) -> CGFloat {
guard let collectionView = collectionView,
let delegate = collectionView.delegate as? UICollectionViewDelegateFlowLayout
else { return 0 }
let itemOriginSize = delegate.collectionView!(collectionView,
layout: self,
sizeForItemAt: indexPath)
return itemWidth / itemOriginSize.width * itemOriginSize.height
}
fileprivate func setMinColumn(h: CGFloat) {
minColumnHeight += h
columnHeights[minColumn] = minColumnHeight
(minColumn, minColumnHeight) = columnHeights.enumerated().min(by: { $0.1 < $1.1 }) ?? (0, 0)
}
}
|