Guest User

Untitled

a guest
Oct 19th, 2017
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.49 KB | None | 0 0
  1. import Foundation
  2. import UIKit
  3.  
  4. struct GalleryLayoutConst {
  5. static let spacing: CGFloat = 1
  6. static let rowMaximumHeight: CGFloat = 150
  7. }
  8.  
  9. protocol GalleryLayoutDelegate: class {
  10. func aspectRatioForIndexPath(_ indexPath: IndexPath) -> CGFloat?
  11. }
  12.  
  13. class GalleryLayout: UICollectionViewLayout {
  14. weak var delegate: GalleryLayoutDelegate?
  15.  
  16. private var cellAttributes: [IndexPath:UICollectionViewLayoutAttributes] = [:]
  17. private var contentSize: CGSize = CGSize.zero
  18.  
  19. override var collectionViewContentSize: CGSize {
  20. return contentSize
  21. }
  22.  
  23. override func prepare() {
  24. guard cellAttributes.isEmpty, let collectionView = collectionView else { return }
  25.  
  26. //Layout Attributes
  27. let collectionViewWidth = collectionView.bounds.size.width
  28. var lastY: CGFloat = 0
  29. var lastX: CGFloat = 0
  30.  
  31. for section in 0..<collectionView.numberOfSections {
  32. for item in 0..<collectionView.numberOfItems(inSection: section) {
  33.  
  34. let ip = IndexPath(item: item, section: section)
  35. let att = UICollectionViewLayoutAttributes(forCellWith: ip)
  36.  
  37. var size: CGSize
  38. if let sizeFromTemp = calculatedItemSizes[ip] {
  39. size = sizeFromTemp
  40. } else {
  41. lastX = 0
  42. lastY = lastY + currentRowHeight + GalleryLayoutConst.spacing
  43. calculateNextRowItemsSize(startFrom: ip, cvWidth: collectionViewWidth)
  44. size = calculatedItemSizes[ip] ?? CGSize(width: 50, height: 50) // ?? only
  45. }
  46.  
  47. att.frame = CGRect(origin: CGPoint(x: lastX, y: lastY), size: size)
  48. cellAttributes[ip] = att
  49. lastX = lastX + size.width + GalleryLayoutConst.spacing
  50. }
  51. }
  52. //ContentSize
  53. contentSize = CGSize(width: collectionViewWidth, height: lastY + currentRowHeight)
  54. }
  55.  
  56. var calculatedItemSizes: [IndexPath: CGSize] = [:]
  57. var currentRowHeight: CGFloat = 0
  58.  
  59. func calculateNextRowItemsSize(startFrom indexPath: IndexPath, cvWidth: CGFloat) {
  60.  
  61. let firstAspRat = aspectRatio(indexPath)
  62. let firstHeihgt = cvWidth / firstAspRat
  63.  
  64. //when the first image occupies an entire line
  65. if firstHeihgt <= GalleryLayoutConst.rowMaximumHeight {
  66. currentRowHeight = firstHeihgt
  67. calculatedItemSizes[indexPath] = CGSize(width: cvWidth, height: firstHeihgt)
  68. return
  69. }
  70.  
  71. var aspRats: [IndexPath: CGFloat] = [indexPath: firstAspRat]
  72. var currIndexPath = indexPath
  73.  
  74. while true {
  75.  
  76. guard let nextIP = nextIndexPath(indexPath: currIndexPath) else {
  77. //when the images have not taken the whole line, but there is no more (we stretch them to the maximum allowed height)
  78. currentRowHeight = GalleryLayoutConst.rowMaximumHeight
  79. calculateForNewHeight(newHeight: currentRowHeight, aspRats: aspRats)
  80. return
  81. }
  82.  
  83. currIndexPath = nextIP
  84. aspRats[currIndexPath] = aspectRatio(currIndexPath)
  85.  
  86. let sumAspectRatios = aspRats.values.reduce(0) {$0 + $1}
  87. let curNewHeight = cvWidth / sumAspectRatios
  88.  
  89. // when the pictures have taken the whole line
  90. if curNewHeight <= GalleryLayoutConst.rowMaximumHeight {
  91. currentRowHeight = curNewHeight
  92. calculateForNewHeight(newHeight: curNewHeight, aspRats: aspRats)
  93. return
  94. }
  95. }
  96. }
  97.  
  98. private func calculateForNewHeight(newHeight: CGFloat, aspRats: [IndexPath: CGFloat]) {
  99. for (indexPath, aspRet) in aspRats {
  100. let newWidth = newHeight * aspRet
  101. calculatedItemSizes[indexPath] = CGSize(width: newWidth, height: newHeight)
  102. }
  103. }
  104.  
  105. private func aspectRatio(_ indexPath: IndexPath) -> CGFloat {
  106. return delegate?.aspectRatioForIndexPath(indexPath) ?? 1
  107. }
  108.  
  109. private func nextIndexPath(indexPath: IndexPath) -> IndexPath? {
  110. guard let cv = collectionView else { return nil }
  111. let currSection = indexPath.section
  112. guard currSection < cv.numberOfSections else { return nil }
  113. let currItem = indexPath.item
  114.  
  115. if indexPath.item < cv.numberOfItems(inSection: currSection) - 1 {
  116. return IndexPath(item: currItem + 1, section: currSection)
  117. }
  118.  
  119. let nextSection = currSection + 1
  120. if nextSection < cv.numberOfSections && cv.numberOfItems(inSection: nextSection) > 0 {
  121. return IndexPath(item: 0, section: currSection + 1)
  122. }
  123. return nil
  124. }
  125.  
  126. override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
  127. var allAttributes: [UICollectionViewLayoutAttributes] = []
  128.  
  129. for (_, attributes) in cellAttributes {
  130. if rect.intersects(attributes.frame) {
  131. allAttributes.append(attributes)
  132. }
  133. }
  134. return allAttributes
  135. }
  136.  
  137. override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
  138. return cellAttributes[indexPath]
  139. }
  140.  
  141. override func invalidateLayout() {
  142. super.invalidateLayout()
  143. calculatedItemSizes = [:]
  144. currentRowHeight = 0
  145. cellAttributes = [:]
  146. contentSize = CGSize.zero
  147. }
  148. }
Add Comment
Please, Sign In to add comment