mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00

git-subtree-dir: submodules/AsyncDisplayKit git-subtree-mainline: d06f423e0ed3df1fed9bd10d79ee312a9179b632 git-subtree-split: 02bedc12816e251ad71777f9d2578329b6d2bef6
247 lines
8.6 KiB
Swift
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;
|
|
}
|
|
}
|