Brak opisu

WaterfallFlowLayout.swift 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //
  2. // WaterfallFlowLayout.swift
  3. // PaiAi
  4. //
  5. // Created by FFIB on 2017/11/13.
  6. // Copyright © 2017年 FFIB. All rights reserved.
  7. //
  8. import UIKit
  9. public final class WaterfallFlowLayout: UICollectionViewLayout {
  10. private var minColumn: Int = 0
  11. private var itemWidth: CGFloat = -1
  12. private var columnHeights = [CGFloat]()
  13. private var minColumnHeight: CGFloat = 0
  14. private(set) var configuration = WaterfallFlowConfiguration()
  15. private var attributesArr = [UICollectionViewLayoutAttributes]()
  16. override public init() {
  17. super.init()
  18. }
  19. convenience init(configuration: WaterfallFlowConfiguration) {
  20. self.init()
  21. self.configuration = configuration
  22. }
  23. required init?(coder aDecoder: NSCoder) {
  24. super.init(coder: aDecoder)
  25. }
  26. override public func prepare() {
  27. super.prepare()
  28. initialize()
  29. }
  30. override public var collectionViewContentSize: CGSize {
  31. return calculateViewSize()
  32. }
  33. override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
  34. guard attributesArr.count <= indexPath.row else {
  35. return attributesArr[indexPath.row]
  36. }
  37. let itemX = calculateItemX()
  38. let itemY = calculateItemY()
  39. let itemHeight = calculateItemHeight(indexPath: indexPath)
  40. let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
  41. attributes.frame = CGRect(x: itemX, y: itemY, width: itemWidth, height: itemHeight)
  42. setMinColumn(h: itemHeight)
  43. return attributes
  44. }
  45. override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
  46. return attributesArr
  47. }
  48. fileprivate func initialize() {
  49. guard collectionView?.numberOfSections == 1,
  50. let itemCount = collectionView?.numberOfItems(inSection: 0),
  51. itemCount != 0 else { return }
  52. let originIndex: Int
  53. if attributesArr.count >= itemCount || itemWidth == -1 {
  54. minColumn = 0
  55. originIndex = 0
  56. minColumnHeight = 0
  57. attributesArr.removeAll()
  58. calculateItemWidth()
  59. columnHeights = Array(repeating: 0, count: configuration.columnCount)
  60. } else {
  61. originIndex = attributesArr.count
  62. }
  63. for i in originIndex..<itemCount {
  64. guard let attributes = layoutAttributesForItem(at: IndexPath(row: i, section: 0))
  65. else { continue }
  66. attributesArr.append(attributes)
  67. }
  68. }
  69. fileprivate func calculateViewSize() -> CGSize {
  70. guard let collectionView = collectionView,
  71. let maxH = columnHeights.max() else { return CGSize.zero }
  72. return CGSize(width: collectionView.bounds.width, height: maxH + configuration.rowSpace)
  73. }
  74. fileprivate func calculateItemX() -> CGFloat {
  75. return CGFloat(minColumn) * itemWidth + CGFloat(minColumn + 1) * configuration.columnSpace
  76. }
  77. fileprivate func calculateItemY() -> CGFloat {
  78. minColumnHeight += configuration.rowSpace
  79. return minColumnHeight
  80. }
  81. fileprivate func calculateItemWidth() {
  82. let width = collectionView?.bounds.width ?? 0
  83. let space = configuration.columnSpace * (CGFloat(configuration.columnCount + 1))
  84. itemWidth = (width - space) / CGFloat(configuration.columnCount)
  85. }
  86. fileprivate func calculateItemHeight(indexPath: IndexPath) -> CGFloat {
  87. guard let collectionView = collectionView,
  88. let delegate = collectionView.delegate as? UICollectionViewDelegateFlowLayout
  89. else { return 0 }
  90. let itemOriginSize = delegate.collectionView!(collectionView,
  91. layout: self,
  92. sizeForItemAt: indexPath)
  93. return itemWidth / itemOriginSize.width * itemOriginSize.height
  94. }
  95. fileprivate func setMinColumn(h: CGFloat) {
  96. minColumnHeight += h
  97. columnHeights[minColumn] = minColumnHeight
  98. (minColumn, minColumnHeight) = columnHeights.enumerated().min(by: { $0.1 < $1.1 }) ?? (0, 0)
  99. }
  100. }