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
8 years ago
|
//
|
||
6 years ago
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||
8 years ago
|
//
|
||
|
|
||
|
import Foundation
|
||
|
|
||
8 years ago
|
protocol GifPickerLayoutDelegate: class {
|
||
|
func imageInfosForLayout() -> [GiphyImageInfo]
|
||
|
}
|
||
|
|
||
8 years ago
|
// A Pinterest-style waterfall layout.
|
||
8 years ago
|
class GifPickerLayout: UICollectionViewLayout {
|
||
8 years ago
|
|
||
8 years ago
|
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) {
|
||
7 years ago
|
notImplemented()
|
||
8 years ago
|
}
|
||
|
|
||
|
override init() {
|
||
|
super.init()
|
||
|
}
|
||
|
|
||
|
// MARK: Methods
|
||
|
|
||
|
override func invalidateLayout() {
|
||
|
super.invalidateLayout()
|
||
|
|
||
|
itemAttributesMap.removeAll()
|
||
|
}
|
||
|
|
||
|
override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
|
||
7 years ago
|
super.invalidateLayout(with: context)
|
||
8 years ago
|
|
||
|
itemAttributesMap.removeAll()
|
||
|
}
|
||
|
|
||
|
override func prepare() {
|
||
|
super.prepare()
|
||
|
|
||
|
guard let collectionView = collectionView else {
|
||
|
return
|
||
|
}
|
||
|
guard let delegate = delegate else {
|
||
|
return
|
||
|
}
|
||
|
|
||
8 years ago
|
let vInset = UInt(5)
|
||
|
let hInset = UInt(5)
|
||
8 years ago
|
let vSpacing = UInt(3)
|
||
|
let hSpacing = UInt(3)
|
||
8 years ago
|
|
||
|
// 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.
|
||
6 years ago
|
let columnCount = UInt(max(2, collectionView.width() / 130))
|
||
8 years ago
|
|
||
|
let totalViewWidth = UInt(collectionView.width())
|
||
8 years ago
|
let hTotalWhitespace = (2 * hInset) + (hSpacing * (columnCount - 1))
|
||
8 years ago
|
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.
|
||
8 years ago
|
let totalHSpacing = totalViewWidth - ((2 * hInset) + (columnCount * columnWidth))
|
||
8 years ago
|
|
||
8 years ago
|
// columnXs are the left edge of each column.
|
||
8 years ago
|
var columnXs = [UInt]()
|
||
8 years ago
|
// columnYs are the top edge of the next cell in each column.
|
||
8 years ago
|
var columnYs = [UInt]()
|
||
8 years ago
|
for columnIndex in 0...columnCount-1 {
|
||
8 years ago
|
var columnX = hInset + (columnWidth * columnIndex)
|
||
8 years ago
|
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.
|
||
8 years ago
|
columnX += ((totalHSpacing * columnIndex) / (columnCount - 1))
|
||
8 years ago
|
}
|
||
|
columnXs.append(columnX)
|
||
8 years ago
|
columnYs.append(vInset)
|
||
8 years ago
|
}
|
||
|
|
||
|
// Always layout all items.
|
||
|
let imageInfos = delegate.imageInfosForLayout()
|
||
8 years ago
|
var contentBottom = vInset
|
||
8 years ago
|
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)
|
||
7 years ago
|
let itemAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
|
||
|
let itemFrame = CGRect(x: CGFloat(cellX), y: CGFloat(cellY), width: CGFloat(cellWidth), height: CGFloat(cellHeight))
|
||
8 years ago
|
itemAttributes.frame = itemFrame
|
||
|
itemAttributesMap[UInt(cellIndex)] = itemAttributes
|
||
|
|
||
|
columnYs[column] = cellY + cellHeight + vSpacing
|
||
8 years ago
|
contentBottom = max(contentBottom, cellY + cellHeight)
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
// Add bottom margin.
|
||
8 years ago
|
let contentHeight = contentBottom + vInset
|
||
7 years ago
|
contentSize = CGSize(width: CGFloat(totalViewWidth), height: CGFloat(contentHeight))
|
||
8 years ago
|
}
|
||
|
|
||
|
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
||
8 years ago
|
return itemAttributesMap.values.filter { itemAttributes in
|
||
|
return itemAttributes.frame.intersects(rect)
|
||
8 years ago
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
8 years ago
|
}
|