Skip to content

Instantly share code, notes, and snippets.

@GeekTree0101
Created June 6, 2018 03:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GeekTree0101/12ea397c5142c97d99b24494774a9557 to your computer and use it in GitHub Desktop.
Save GeekTree0101/12ea397c5142c97d99b24494774a9557 to your computer and use it in GitHub Desktop.
Texture Chat Application UICollectionView Flow Layout Example
import Foundation
import AsyncDisplayKit
class ChatFlowLayoutExample: UICollectionViewFlowLayout {
private var topVisibleItem = Int.max
private var bottomVisibleItem = -Int.max
private var offset: CGFloat = 0.0
private var visibleAttributes: [UICollectionViewLayoutAttributes]?
private var isPrepend: Bool = false
override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Reset offset and prepend scope
visibleAttributes = super.layoutAttributesForElements(in: rect)
offset = 0.0
isPrepend = false
return visibleAttributes
}
override open func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
guard let collectionView = self.collectionView else { return }
guard let visibleAttributes = self.visibleAttributes else { return }
// Calculate Bottom and Top Visible Item Count
bottomVisibleItem = -Int.max
topVisibleItem = Int.max
var containerHeight: CGFloat = collectionView.frame.size.height
containerHeight -= collectionView.contentInset.top
containerHeight -= collectionView.contentInset.bottom
let container = CGRect(x: collectionView.contentOffset.x,
y: collectionView.contentOffset.y,
width: collectionView.frame.size.width,
height: containerHeight)
for attributes in visibleAttributes {
if attributes.frame.intersects(container) {
let item = attributes.indexPath.item
if item < topVisibleItem {
topVisibleItem = item
}
if item > bottomVisibleItem {
bottomVisibleItem = item
}
}
}
super.prepare(forCollectionViewUpdates: updateItems)
// Check: Initial Load or Load More
let isInitialLoading: Bool = bottomVisibleItem + topVisibleItem == 0
// Chack: Pre-Append or Append
if updateItems.first?.indexPathAfterUpdate?.item ?? -1 == 0,
updateItems.first?.updateAction == .insert,
!isInitialLoading {
self.isPrepend = true
} else {
return
}
// Calculate Offset
offset = updateItems.filter { $0.updateAction == .insert }
.map { $0.indexPathAfterUpdate }.filterNil()
.filter { topVisibleItem + updateItems.count > $0.item }
.map { self.layoutAttributesForItem(at: $0) }.filterNil()
.map { $0.size.height + self.minimumLineSpacing }
.reduce(0.0, { $0 + $1 })
let contentHeight = collectionView.contentSize.height
var frameHeight = collectionView.frame.size.height
frameHeight -= collectionView.contentInset.top
frameHeight -= collectionView.contentInset.bottom
guard contentHeight + offset > frameHeight else {
// Exception
self.isPrepend = false
return
}
CATransaction.begin()
CATransaction.setDisableActions(true)
}
override open func finalizeCollectionViewUpdates() {
guard let collectionView = self.collectionView, isPrepend else { return }
// Adjust Content Offset
let newContentOffset = CGPoint(x: collectionView.contentOffset.x,
y: collectionView.contentOffset.y + self.offset)
collectionView.contentOffset = newContentOffset
CATransaction.commit()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment