瀑布流
import UIKit
public protocol SPWaterflowLayoutDelegate: NSObjectProtocol {
func waterflowLayout(view: SPWaterflowLayout, heightForItemAtIndex: IndexPath, itemWidth: CGFloat) -> CGFloat
}
public class SPWaterflowLayout: UICollectionViewFlowLayout {
weak open var delegate: SPWaterflowLayoutDelegate?
public var columnCount: NSInteger = 2
public var columnMargin: CGFloat = 10
public var rowMargin: CGFloat = 10
public var edgeInsets: UIEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
private var attrsArray = [UICollectionViewLayoutAttributes]()
private var columnHeights = [CGFloat]()
private var contentHeight: CGFloat = 0
}
extension SPWaterflowLayout {
public override func prepare() {
super.prepare()
contentHeight = 0
columnHeights.removeAll()
for _ in 0..<columnCount {
columnHeights.append(edgeInsets.top)
}
attrsArray.removeAll()
guard let sections = collectionView?.numberOfSections else { return }
for section in 0..<sections {
guard let count = collectionView?.numberOfItems(inSection: section) else { return }
for i in 0..<count {
if let attrs = layoutAttributesForItem(at: IndexPath(item: i, section: section)) {
attrsArray.append(attrs)
}
}
}
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attrsArray
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)
guard let collectionViewWidth = collectionView?.frame.width else { return attrs }
let width = (collectionViewWidth - edgeInsets.left - edgeInsets.right - (CGFloat(columnCount) - 1) * columnMargin) / CGFloat(columnCount)
guard let height = delegate?.waterflowLayout(view: self, heightForItemAtIndex: indexPath, itemWidth: width) else { return attrs }
var destColumn: NSInteger = 0
var minColumnHeight: CGFloat = columnHeights[0]
for i in 0..<columnCount {
let columnHeight = columnHeights[i]
if minColumnHeight > columnHeight {
minColumnHeight = columnHeight
destColumn = i
}
}
let x = edgeInsets.left + CGFloat(destColumn) * (width + columnMargin)
var y = minColumnHeight
if y != edgeInsets.top {
y += rowMargin
}
attrs.frame = CGRect(x: x, y: y, width: width, height: height)
columnHeights[destColumn] = attrs.frame.maxY
let columnHeight = columnHeights[destColumn]
if contentHeight < columnHeight {
contentHeight = columnHeight
}
return attrs
}
public override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: contentHeight + edgeInsets.bottom)
}
}
标签
import UIKit
public protocol SPTagsListLayoutDelegate: NSObjectProtocol {
func tagsListLayout(view: SPTagsListLayout, widthForItemAtIndex: IndexPath, itemHeight: CGFloat) -> CGFloat
func tagsListLayout(view: SPTagsListLayout, maxHeight: CGFloat)
}
public class SPTagsListLayout: UICollectionViewFlowLayout {
weak open var delegate: SPTagsListLayoutDelegate?
public var itemHeight: CGFloat = 25
public var columnMargin: CGFloat = 10
public var rowMargin: CGFloat = 10
public var edgeInsets: UIEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
private var attrsArray = [UICollectionViewLayoutAttributes]()
private var preAttrs = [String : UICollectionViewLayoutAttributes]()
private var maxY: CGFloat = 0
}
extension SPTagsListLayout {
public override func prepare() {
super.prepare()
attrsArray.removeAll()
guard let sections = collectionView?.numberOfSections else { return }
for section in 0..<sections {
guard let count = collectionView?.numberOfItems(inSection: section) else { return }
for i in 0..<count {
if let attrs = layoutAttributesForItem(at: IndexPath(item: i, section: section)) {
attrsArray.append(attrs)
}
}
}
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attrsArray
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)
guard let collectionViewWidth = collectionView?.frame.width,
var currentWidth = delegate?.tagsListLayout(view: self, widthForItemAtIndex: indexPath, itemHeight: itemHeight) else { return attrs }
currentWidth = min(currentWidth, collectionViewWidth - edgeInsets.left - edgeInsets.right)
let key = "preAttrs_key_\(indexPath.section)_\(indexPath.row)"
var x: CGFloat = edgeInsets.left
var y: CGFloat = edgeInsets.top
if indexPath.row - 1 >= 0 {
let preKey = "preAttrs_key_\(indexPath.section)_\(indexPath.row - 1)"
if let preAttr = preAttrs[preKey] {
y = preAttr.frame.origin.y
x = (preAttr.frame.maxX + columnMargin)
}
}
if x + currentWidth > collectionViewWidth - edgeInsets.right {
x = edgeInsets.left
y += (rowMargin + itemHeight)
}
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attr.frame = CGRect(x: x, y: y, width: currentWidth, height: itemHeight)
preAttrs[key] = attr
maxY = attr.frame.maxY
return attr
}
public override var collectionViewContentSize: CGSize {
delegate?.tagsListLayout(view: self, maxHeight: maxY + edgeInsets.bottom)
return CGSize(width: 0, height: maxY + edgeInsets.bottom)
}
}
标签使用 :
代理 func tagsListLayout(view: SPTagsListLayout, maxHeight: CGFloat)
返回的 maxHeight
不要超过 UICollectionView
的高度
import UIKit
class ViewController: UIViewController, SPTagsListLayoutDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private lazy var contentView: UIView = {
let view = UIView(frame: CGRect(x: 20, y: 100, width: UIScreen.main.bounds.width - 40, height: 0))
view.clipsToBounds = true
return view
}()
private lazy var collectionView: UICollectionView = {
let layout = SPTagsListLayout()
layout.delegate = self
let view = UICollectionView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: UIScreen.main.bounds.height), collectionViewLayout: layout)
view.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCellID")
view.delegate = self
view.dataSource = self
view.backgroundColor = .white
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
view.addSubview(contentView)
contentView.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "UICollectionViewCellID", for: indexPath)
cell.backgroundColor = UIColor(red: CGFloat(arc4random() % 256) / 255.0, green: CGFloat(arc4random() % 256) / 255.0, blue: CGFloat(arc4random() % 256) / 255.0, alpha: 1.0)
return cell
}
func tagsListLayout(view: SPTagsListLayout, widthForItemAtIndex: IndexPath, itemHeight: CGFloat) -> CGFloat {
return CGFloat(arc4random_uniform(100) + 10)
}
func tagsListLayout(view: SPTagsListLayout, maxHeight: CGFloat) {
contentView.frame.size.height = maxHeight
}
}