Peter 9bc996374f Add 'submodules/AsyncDisplayKit/' from commit '02bedc12816e251ad71777f9d2578329b6d2bef6'
git-subtree-dir: submodules/AsyncDisplayKit
git-subtree-mainline: d06f423e0ed3df1fed9bd10d79ee312a9179b632
git-subtree-split: 02bedc12816e251ad71777f9d2578329b6d2bef6
2019-06-11 18:42:43 +01:00

247 lines
8.6 KiB
Swift

//
// MosaicCollectionViewLayout.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
import UIKit
import AsyncDisplayKit
protocol MosaicCollectionViewLayoutDelegate: ASCollectionDelegate {
func collectionView(_ collectionView: UICollectionView, layout: MosaicCollectionViewLayout, originalItemSizeAtIndexPath: IndexPath) -> CGSize
}
class MosaicCollectionViewLayout: UICollectionViewFlowLayout {
var numberOfColumns: Int
var columnSpacing: CGFloat
var _sectionInset: UIEdgeInsets
var interItemSpacing: UIEdgeInsets
var headerHeight: CGFloat
var _columnHeights: [[CGFloat]]?
var _itemAttributes = [[UICollectionViewLayoutAttributes]]()
var _headerAttributes = [UICollectionViewLayoutAttributes]()
var _allAttributes = [UICollectionViewLayoutAttributes]()
required override init() {
self.numberOfColumns = 2
self.columnSpacing = 10.0
self.headerHeight = 44.0 //viewcontroller
self._sectionInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
self.interItemSpacing = UIEdgeInsetsMake(10.0, 0, 10.0, 0)
super.init()
self.scrollDirection = .vertical
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public var delegate : MosaicCollectionViewLayoutDelegate?
override func prepare() {
super.prepare()
guard let collectionView = self.collectionView else { return }
_itemAttributes = []
_allAttributes = []
_headerAttributes = []
_columnHeights = []
var top: CGFloat = 0
let numberOfSections: NSInteger = collectionView.numberOfSections
for section in 0 ..< numberOfSections {
let numberOfItems = collectionView.numberOfItems(inSection: section)
top += _sectionInset.top
if (headerHeight > 0) {
let headerSize: CGSize = self._headerSizeForSection(section: section)
let attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: NSIndexPath(row: 0, section: section) as IndexPath)
attributes.frame = CGRect(x: _sectionInset.left, y: top, width: headerSize.width, height: headerSize.height)
_headerAttributes.append(attributes)
_allAttributes.append(attributes)
top = attributes.frame.maxY
}
_columnHeights?.append([]) //Adding new Section
for _ in 0 ..< self.numberOfColumns {
self._columnHeights?[section].append(top)
}
let columnWidth = self._columnWidthForSection(section: section)
_itemAttributes.append([])
for idx in 0 ..< numberOfItems {
let columnIndex: Int = self._shortestColumnIndexInSection(section: section)
let indexPath = IndexPath(item: idx, section: section)
let itemSize = self._itemSizeAtIndexPath(indexPath: indexPath);
let xOffset = _sectionInset.left + (columnWidth + columnSpacing) * CGFloat(columnIndex)
let yOffset = _columnHeights![section][columnIndex]
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemSize.width, height: itemSize.height)
_columnHeights?[section][columnIndex] = attributes.frame.maxY + interItemSpacing.bottom
_itemAttributes[section].append(attributes)
_allAttributes.append(attributes)
}
let columnIndex: Int = self._tallestColumnIndexInSection(section: section)
top = (_columnHeights?[section][columnIndex])! - interItemSpacing.bottom + _sectionInset.bottom
for idx in 0 ..< _columnHeights![section].count {
_columnHeights![section][idx] = top
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
{
var includedAttributes: [UICollectionViewLayoutAttributes] = []
// Slow search for small batches
for attribute in _allAttributes {
if (attribute.frame.intersects(rect)) {
includedAttributes.append(attribute)
}
}
return includedAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
{
guard indexPath.section < _itemAttributes.count,
indexPath.item < _itemAttributes[indexPath.section].count
else {
return nil
}
return _itemAttributes[indexPath.section][indexPath.item]
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
{
if (elementKind == UICollectionElementKindSectionHeader) {
return _headerAttributes[indexPath.section]
}
return nil
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if (!(self.collectionView?.bounds.size.equalTo(newBounds.size))!) {
return true;
}
return false;
}
func _widthForSection (section: Int) -> CGFloat
{
return self.collectionView!.bounds.size.width - _sectionInset.left - _sectionInset.right;
}
func _columnWidthForSection(section: Int) -> CGFloat
{
return (self._widthForSection(section: section) - ((CGFloat(numberOfColumns - 1)) * columnSpacing)) / CGFloat(numberOfColumns)
}
func _itemSizeAtIndexPath(indexPath: IndexPath) -> CGSize
{
var size = CGSize(width: self._columnWidthForSection(section: indexPath.section), height: 0)
let originalSize = self.delegate!.collectionView(self.collectionView!, layout:self, originalItemSizeAtIndexPath:indexPath)
if (originalSize.height > 0 && originalSize.width > 0) {
size.height = originalSize.height / originalSize.width * size.width
}
return size
}
func _headerSizeForSection(section: Int) -> CGSize
{
return CGSize(width: self._widthForSection(section: section), height: headerHeight)
}
override var collectionViewContentSize: CGSize
{
var height: CGFloat = 0
if ((_columnHeights?.count)! > 0) {
if (_columnHeights?[(_columnHeights?.count)!-1].count)! > 0 {
height = (_columnHeights?[(_columnHeights?.count)!-1][0])!
}
}
return CGSize(width: self.collectionView!.bounds.size.width, height: height)
}
func _tallestColumnIndexInSection(section: Int) -> Int
{
var index: Int = 0;
var tallestHeight: CGFloat = 0;
_ = _columnHeights?[section].enumerated().map { (idx,height) in
if (height > tallestHeight) {
index = idx;
tallestHeight = height
}
}
return index
}
func _shortestColumnIndexInSection(section: Int) -> Int
{
var index: Int = 0;
var shortestHeight: CGFloat = CGFloat.greatestFiniteMagnitude
_ = _columnHeights?[section].enumerated().map { (idx,height) in
if (height < shortestHeight) {
index = idx;
shortestHeight = height
}
}
return index
}
}
class MosaicCollectionViewLayoutInspector: NSObject, ASCollectionViewLayoutInspecting
{
func collectionView(_ collectionView: ASCollectionView, constrainedSizeForNodeAt indexPath: IndexPath) -> ASSizeRange {
let layout = collectionView.collectionViewLayout as! MosaicCollectionViewLayout
return ASSizeRangeMake(CGSize.zero, layout._itemSizeAtIndexPath(indexPath: indexPath))
}
func collectionView(_ collectionView: ASCollectionView, constrainedSizeForSupplementaryNodeOfKind: String, at atIndexPath: IndexPath) -> ASSizeRange
{
let layout = collectionView.collectionViewLayout as! MosaicCollectionViewLayout
return ASSizeRange.init(min: CGSize.zero, max: layout._headerSizeForSection(section: atIndexPath.section))
}
/**
* Asks the inspector for the number of supplementary sections in the collection view for the given kind.
*/
func collectionView(_ collectionView: ASCollectionView, numberOfSectionsForSupplementaryNodeOfKind kind: String) -> UInt {
if (kind == UICollectionElementKindSectionHeader) {
return UInt((collectionView.dataSource?.numberOfSections!(in: collectionView))!)
} else {
return 0
}
}
/**
* Asks the inspector for the number of supplementary views for the given kind in the specified section.
*/
func collectionView(_ collectionView: ASCollectionView, supplementaryNodesOfKind kind: String, inSection section: UInt) -> UInt {
if (kind == UICollectionElementKindSectionHeader) {
return 1
} else {
return 0
}
}
func scrollableDirections() -> ASScrollDirection {
return ASScrollDirectionVerticalDirections;
}
}