mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			144 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			144 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Swift
		
	
| //
 | |
| //  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| import Foundation
 | |
| 
 | |
| protocol GifPickerLayoutDelegate: class {
 | |
|     func imageInfosForLayout() -> [GiphyImageInfo]
 | |
| }
 | |
| 
 | |
| // A Pinterest-style waterfall layout.
 | |
| class GifPickerLayout: UICollectionViewLayout {
 | |
| 
 | |
|     public weak var delegate: GifPickerLayoutDelegate?
 | |
| 
 | |
|     private var itemAttributesMap = [UInt: UICollectionViewLayoutAttributes]()
 | |
| 
 | |
|     private var contentSize = CGSize.zero
 | |
| 
 | |
|     // MARK: Initializers and Factory Methods
 | |
| 
 | |
|     @available(*, unavailable, message:"use other constructor instead.")
 | |
|     required init?(coder aDecoder: NSCoder) {
 | |
|         notImplemented()
 | |
|     }
 | |
| 
 | |
|     override init() {
 | |
|         super.init()
 | |
|     }
 | |
| 
 | |
|     // MARK: Methods
 | |
| 
 | |
|     override func invalidateLayout() {
 | |
|         super.invalidateLayout()
 | |
| 
 | |
|         itemAttributesMap.removeAll()
 | |
|     }
 | |
| 
 | |
|     override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
 | |
|         super.invalidateLayout(with: context)
 | |
| 
 | |
|         itemAttributesMap.removeAll()
 | |
|     }
 | |
| 
 | |
|     override func prepare() {
 | |
|         super.prepare()
 | |
| 
 | |
|         guard let collectionView = collectionView else {
 | |
|             return
 | |
|         }
 | |
|         guard let delegate = delegate else {
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         let vInset = UInt(5)
 | |
|         let hInset = UInt(5)
 | |
|         let vSpacing = UInt(3)
 | |
|         let hSpacing = UInt(3)
 | |
| 
 | |
|         // We  use 2 or 3 columns, depending on the device.
 | |
|         // 2 columns will show fewer GIFs at a time,
 | |
|         // but use less network & be a more responsive experience.
 | |
|         let columnCount = UInt(2)
 | |
| 
 | |
|         let totalViewWidth = UInt(collectionView.width())
 | |
|         let hTotalWhitespace = (2 * hInset) + (hSpacing * (columnCount - 1))
 | |
|         let hRemainderSpace = totalViewWidth - hTotalWhitespace
 | |
|         let columnWidth = UInt(hRemainderSpace / columnCount)
 | |
|         // We want to unevenly distribute the hSpacing between the columns
 | |
|         // so that the left and right margins are equal, which is non-trivial
 | |
|         // due to rounding error.
 | |
|         let totalHSpacing = totalViewWidth - ((2 * hInset) + (columnCount * columnWidth))
 | |
| 
 | |
|         // columnXs are the left edge of each column.
 | |
|         var columnXs = [UInt]()
 | |
|         // columnYs are the top edge of the next cell in each column.
 | |
|         var columnYs = [UInt]()
 | |
|         for columnIndex in 0...columnCount-1 {
 | |
|             var columnX = hInset + (columnWidth * columnIndex)
 | |
|             if columnCount > 1 {
 | |
|                 // We want to unevenly distribute the hSpacing between the columns
 | |
|                 // so that the left and right margins are equal, which is non-trivial
 | |
|                 // due to rounding error.
 | |
|                 columnX += ((totalHSpacing * columnIndex) / (columnCount - 1))
 | |
|             }
 | |
|             columnXs.append(columnX)
 | |
|             columnYs.append(vInset)
 | |
|         }
 | |
| 
 | |
|         // Always layout all items.
 | |
|         let imageInfos = delegate.imageInfosForLayout()
 | |
|         var contentBottom = vInset
 | |
|         for (cellIndex, imageInfo) in imageInfos.enumerated() {
 | |
|             // Select a column by finding the "highest, leftmost" column.
 | |
|             var column = 0
 | |
|             var cellY = columnYs[column]
 | |
|             for (columnValue, columnYValue) in columnYs.enumerated() {
 | |
|                 if columnYValue < cellY {
 | |
|                     column = columnValue
 | |
|                     cellY = columnYValue
 | |
|                 }
 | |
|             }
 | |
|             let cellX = columnXs[column]
 | |
|             let cellWidth = columnWidth
 | |
|             let cellHeight = UInt(columnWidth * imageInfo.originalRendition.height / imageInfo.originalRendition.width)
 | |
| 
 | |
|             let indexPath = NSIndexPath(row: cellIndex, section: 0)
 | |
|             let itemAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
 | |
|             let itemFrame = CGRect(x: CGFloat(cellX), y: CGFloat(cellY), width: CGFloat(cellWidth), height: CGFloat(cellHeight))
 | |
|             itemAttributes.frame = itemFrame
 | |
|             itemAttributesMap[UInt(cellIndex)] = itemAttributes
 | |
| 
 | |
|             columnYs[column] = cellY + cellHeight + vSpacing
 | |
|             contentBottom = max(contentBottom, cellY + cellHeight)
 | |
|         }
 | |
| 
 | |
|         // Add bottom margin.
 | |
|         let contentHeight = contentBottom + vInset
 | |
|         contentSize = CGSize(width: CGFloat(totalViewWidth), height: CGFloat(contentHeight))
 | |
|     }
 | |
| 
 | |
|     override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
 | |
|         return itemAttributesMap.values.filter { itemAttributes in
 | |
|             return itemAttributes.frame.intersects(rect)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
 | |
|         let result = itemAttributesMap[UInt(indexPath.row)]
 | |
|         return result
 | |
|     }
 | |
| 
 | |
|     override var collectionViewContentSize: CGSize {
 | |
|         return contentSize
 | |
|     }
 | |
| 
 | |
|     override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
 | |
|         guard let collectionView = collectionView else {
 | |
|             return false
 | |
|         }
 | |
|         return collectionView.width() != newBounds.size.width
 | |
|     }
 | |
| }
 |