adding a custom header to ray wenderlich Pinterest Layout - ios

I used the Pinterest layout file provided from Ray Wenderlich and I implemented the desired layout for cells perfectly, however on a CollectionViewController where I am adding a custom header, I want to add the Pinterest layer below the custom header.
But the Pinterest layer is overriding the custom header and I can no longer find the custom header.
Any suggestion on how I can solve this would be greatly appreciated.
The Pinterest Layout file is below:
import UIKit
protocol CustomLayoutDelegate: class {
// 1. Method to ask the delegate for the height of the image
func collectionView(_ collectionView:UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
}
class CustomLayout: UICollectionViewFlowLayout {
//1. Pinterest Layout Delegate
weak var delegate: CustomLayoutDelegate!
//2. Configurable properties
fileprivate var numberOfColumns = 2
fileprivate var cellPadding: CGFloat = 5
//3. Array to keep a cache of attributes.
fileprivate var cache = [UICollectionViewLayoutAttributes]()
//4. Content height and size
fileprivate var contentHeight: CGFloat = 0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// 1. Only calculate once
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
// 2. Pre-Calculates the X Offset for every column and adds an array to increment the currently max Y Offset for each column
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)
// 3. Iterates through the list of items in the first section
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// 4. Asks the delegate for the height of the picture and the annotation and calculates the cell frame.
let photoHeight = delegate.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath)
let height = cellPadding * 2 + photoHeight
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// 5. Creates an UICollectionViewLayoutItem with the frame and add it to the cache
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// 6. Updates the collection view content height
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}

You have to append an attributes for your header before append the first item
Beside that set the yOffset first start from your Header Height
var yOffset = [yourHeaderHeight, yourHeaderHeight]
Add this code between section after the indexPath in section 3
if item == 0 {
let headerAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: indexPath)
headerAttributes.frame = CGRect(x: 0, y: 0, width: yourHeaderWidth, height: yourHeaderHeight)
cache.append(headerAttributes)
}

Related

Pin Cells to Top of UICollectionView Edge

I have a horizontally scrolling uicollectionview. I dynamically change each cell's height based on the length of a subview uilabel text string. At the end, I set the uicollectionview height constraint to equal the longest cell height.
This result in a uicollectionview where each cell varies in height and prevents me from achieving my goal: get all cells' tops to align and be pinned to the top edge of the uicollectionview.
Is anyone able to lend a hand here? I understand I might have to create a custom layout for this, but have no clue where to start and could not find any references to instruct. Any guidance would be appreciated!
easy to do it with custom UICollectionViewLayout,
put it simple:
you just assign the frame of each item manually.
Notice the logic in func prepare()
The rest method are just common mode.
for variable item height, you can use a delegate to fetch the value.
enum CommonComponent: String {
case cell
case header
var kind: String {
switch self{
case .header:
return "UICollectionElementKindSectionHeader"
default:
return ""
}
}
}
protocol CustomLayoutProxy: AnyObject {
func getHeightFor(indexPath ip: IndexPath) -> CGFloat
}
class CustomLayout: UICollectionViewLayout {
weak var delegate: CustomLayoutProxy?
override public var collectionViewContentSize: CGSize {
CGSize(width: collectionViewWidth, height: contentHeight)
}
// MARK: - Properties
private var contentHeight = CGFloat.zero
private var cache = [CommonComponent: [IndexPath: UICollectionViewLayoutAttributes]]()
private var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
private var collectionViewHeight: CGFloat {
collectionView!.frame.height
}
private var collectionViewWidth: CGFloat {
collectionView!.frame.width
}
let maxItemSize = CGSize(width: 40, height: 40)
}
// MARK: - LAYOUT CORE PROCESS
extension CustomLayout{
override public func prepare() {
guard let collectionView = collectionView else {
return
}
prepareCache()
var cellX: CGFloat = 16
let countOne = collectionView.numberOfItems(inSection: 0)
let headerIP = IndexPath(item: 0, section: 0)
let headH: CGFloat = 58
let headerAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: CommonComponent.header.kind, with: headerIP)
headerAttributes.frame = CGRect(x: 0, y: contentHeight, width: UI.std.width, height: headH)
cache[.header]?[headerIP] = headerAttributes
contentHeight += headH
for item in 0 ..< countOne{
let cellIndexPath = IndexPath(item: item, section: 0)
let attributes = UICollectionViewLayoutAttributes(forCellWith: cellIndexPath)
var size = maxItemSize
if let h = delegate?.getHeightFor(indexPath: cellIndexPath){
size.height = h
}
attributes.frame = CGRect( x: cellX, y: contentHeight, width: size.width, height: size.height )
contentHeight += exceedUp(origin: &cellX, limit: collectionViewWidth)
cache[.cell]?[cellIndexPath] = attributes
}
contentHeight += (maxItemSize.height + 16)
}
private func prepareCache() {
contentHeight = 0
cache.removeAll(keepingCapacity: true)
cache[.cell] = [IndexPath: UICollectionViewLayoutAttributes]()
cache[.header] = [IndexPath: UICollectionViewLayoutAttributes]()
}
}
//MARK: - PROVIDING ATTRIBUTES TO THE COLLECTIONVIEW
extension CustomLayout{
public override func layoutAttributesForSupplementaryView(ofKind ComponentKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
switch ComponentKind {
case CommonComponent.header.kind:
return cache[.header]?[indexPath]
default:
return nil
}
}
override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[.cell]?[indexPath]
}
override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let _ = collectionView else { return nil }
visibleLayoutAttributes.removeAll(keepingCapacity: true)
for (_, infos) in cache {
for (_, attributes) in infos {
attributes.transform = .identity
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
}
return visibleLayoutAttributes
}
func exceedUp(origin x: inout CGFloat, limit widthX: CGFloat) -> CGFloat{
var exceedH: CGFloat = 0
let lineSpacing: CGFloat = 20
let interitemSpacing: CGFloat = 16
let contentEdge = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
if x + maxItemSize.width * 2 + interitemSpacing + contentEdge.right < widthX + 1{
x += maxItemSize.width + interitemSpacing
}
else{
x = contentEdge.left
exceedH = (maxItemSize.height + lineSpacing)
}
return exceedH
}
}
For more example , you can check my github repo
BoxDengJZ/UICollectionViewLeftAlignedLayout
coyingcat/CardCollection

UICollectionViewFlowLayout with horizontal scroll

I really want to understand what is preventing my collection view to be horizontal?
The code is from online tutorial for expanding Cells when scrolling.
I have set the layout to custom
Have set the class to MyClassFlowLayout in Storyboard
Have set scrollDirection = .horizontal in prepare() method
But the collection view is still vertical.
struct MyLayoutConstants {
struct Cell {
// The height of the non-featured cell
static let standardHeight: CGFloat = 50
// The height of the first visible cell
static let featuredHeight: CGFloat = 150
}
}
import UIKit
class MyClassFlowLayout: UICollectionViewFlowLayout {
// The amount the user needs to scroll before the featured cell changes
let dragOffset: CGFloat = 80.0
var cache: [UICollectionViewLayoutAttributes] = []
// Returns the item index of the currently featured cell
var featuredItemIndex: Int {
// Use max to make sure the featureItemIndex is never < 0
return max(0, Int(collectionView!.contentOffset.y / dragOffset))
}
// Returns a value between 0 and 1 that represents how close the next cell is to becoming the featured cell
var nextItemPercentageOffset: CGFloat {
return (collectionView!.contentOffset.x / dragOffset) - CGFloat(featuredItemIndex)
}
// Returns the width of the collection view
var width: CGFloat {
return collectionView!.bounds.width
}
// Returns the height of the collection view
var height: CGFloat {
return collectionView!.bounds.height
}
// Returns the number of items in the collection view
var numberOfItems: Int {
return collectionView!.numberOfItems(inSection: 0)
}
}
extension MyClassFlowLayout {
// Return the size of all the content in the collection view
override var collectionViewContentSize : CGSize {
let contentHeight = (CGFloat(numberOfItems) * dragOffset) + (height - dragOffset)
return CGSize(width: width, height: contentHeight)
}
override func prepare() {
//-----------------------------------------------------------------
scrollDirection = .horizontal
//----------------------------------------------------------------
cache.removeAll(keepingCapacity: false)
let standardHeight = MyLayoutConstants.Cell.standardHeight
let featuredHeight = MyLayoutConstants.Cell.featuredHeight
var frame = CGRect.zero
var y: CGFloat = 0
for item in 0..<numberOfItems {
let indexPath = IndexPath(item: item, section: 0)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
// Important because each cell has to slide over the top of the previous one
attributes.zIndex = item
// Initially set the height of the cell to the standard height
var height = standardHeight
if indexPath.item == featuredItemIndex {
// The featured cell
let yOffset = standardHeight * nextItemPercentageOffset
y = collectionView!.contentOffset.y - yOffset
height = featuredHeight
} else if indexPath.item == (featuredItemIndex + 1) && indexPath.item != numberOfItems {
// The cell directly below the featured cell, which grows as the user scrolls
let maxY = y + standardHeight
height = standardHeight + max((featuredHeight - standardHeight) * nextItemPercentageOffset, 0)
y = maxY - height
}
frame = CGRect(x: 0, y: y, width: width, height: height)
attributes.frame = frame
cache.append(attributes)
y = frame.maxY
}
}
// Return all attributes in the cache whose frame intersects with the rect passed to the method
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in cache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
// Return the content offset of the nearest cell which achieves the nice snapping effect, similar to a paged UIScrollView
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let itemIndex = round(proposedContentOffset.y / dragOffset)
let yOffset = itemIndex * dragOffset
scrollDirection = .horizontal
return CGPoint(x: 0, y: yOffset)
}
// Return true so that the layout is continuously invalidated as the user scrolls
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}

Center different height CollectionView items

I followed this tutorial: https://www.raywenderlich.com/164608/uicollectionview-custom-layout-tutorial-pinterest-2
and came up with the following code.
My question is now, how I can center the items of my collection view. Every item has a different height. (I have vertical scrolling enabled.)
class DynamicLayout: UICollectionViewFlowLayout {
var delegate: DynamicLayoutDelegate?
var numberOfColumns = 2
fileprivate var cellPadding: CGFloat = 6
fileprivate var contentHeight: CGFloat = 0
var xOffset = 0
fileprivate var contentWidth: CGFloat {
guard let c = collectionView else { return 0 }
return c.bounds.width - (c.contentInset.left + c.contentInset.right)
}
fileprivate var cache = [UICollectionViewLayoutAttributes]()
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
override func prepare() {
guard cache.isEmpty == true, let collectionView = collectionView else { return }
// let columnWidth = contentWidth / CGFloat(numberOfColumns)
let columnWidth = CGFloat(310)
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)
// 3. Iterates through the list of items in the first section
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// 4. Asks the delegate for the height of the picture and the annotation and calculates the cell frame.
let cellHeight = delegate?.collectionView(collectionView, heightFor: indexPath)
let height = cellPadding * 2 + cellHeight!
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
// let totalCellWidth = CGFloat(310 * numberOfColumns)
// let totalSpacingWidth = cellPadding * CGFloat(numberOfColumns - 1)
// let leftInset = (collectionView.frame.width - CGFloat(totalCellWidth + totalSpacingWidth)) / 2
// let leftInset = collectionView.frame.width - 310 / 2
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// 5. Creates an UICollectionViewLayoutItem with the frame and add it to the cache
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// 6. Updates the collection view content height
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item] }
}
protocol DynamicLayoutDelegate: class {
func collectionView(_ collectionView:UICollectionView, heightFor indexPath:IndexPath) -> CGFloat
}
The problem is, that I don't want to have empty space below and above smaller cells.
Screenshot of the problem:

Custom UICollectionViewLayout passing layoutattributes for non-existent indexpath

I'm trying to create a custom UICollectionViewFlowLayout where items can be displayed either vertically or horizontally (see below)
Vertical:
|S|S|S|
|1|4|7|
|2|5|8|
|3|6|9|
Horizontal:
|S|1|2|3|
|S|4|5|6|
|S|7|8|9|
However, I cannot seem to get the vertical layout to work properly. The horizontal layout works just fine but every time I want to use the vertical layout it just crashes with the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x7ffcf597fa60> {length = 2, path = 1 - 6}'
When I use the same data and force it to use the horizontal layout it works just fine which leads me to believe I'm doing something wrong with the layout attributes.
My custom flow layout class:
import UIKit
class CustomCollectionViewLayout: UICollectionViewFlowLayout {
var horizontalInset = 0.0 as CGFloat
var verticalInset = 0.0 as CGFloat
var minimumItemWidth = 0.0 as CGFloat
var maximumItemWidth = 0.0 as CGFloat
var itemHeight = 0.0 as CGFloat
var itemWidth = 0.0 as CGFloat
var initialItemSizeSet: Bool = false
var _layoutAttributes = Dictionary<String, UICollectionViewLayoutAttributes>()
var _contentSize = CGSize.zero
var transposed: Bool
init(transposed: Bool) {
self.transposed = transposed
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepare() {
super.prepare()
_layoutAttributes = Dictionary<String, UICollectionViewLayoutAttributes>()
_layoutAttributes.removeAll()
let path = IndexPath(item: 0, section: 0)
let attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: path)
let headerHeight = CGFloat(self.itemHeight / 4)
attributes.frame = CGRect(x: 0, y: 0, width: self.collectionView!.frame.size.width, height: headerHeight)
let headerKey = layoutKeyForHeaderAtIndexPath(path)
_layoutAttributes[headerKey] = attributes
let numberOfSections = (self.collectionView!.numberOfSections)-1
var yOffset = headerHeight
var xOffset = self.horizontalInset
var largestNumberOfItems = 0
for section in 0...numberOfSections {
let numberOfItems = self.collectionView!.numberOfItems(inSection: section)
if(numberOfItems > largestNumberOfItems){
largestNumberOfItems = numberOfItems
}
for item in 0...numberOfItems {
let indexPath = IndexPath(item: item, section: section)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
var itemSize = CGSize.zero
//Every cell has a fixed size
if(!initialItemSizeSet){
self.itemWidth = 75
self.itemHeight = 75
initialItemSizeSet = true
}
itemSize.width = itemWidth
itemSize.height = itemHeight
attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemSize.width, height: itemSize.height).integral
print("Created frame with xOffset \(xOffset), yOffset \(yOffset), width \(itemSize.width) and height \(itemSize.height)")
let key = layoutKeyForIndexPath(indexPath)
print("Attached indexPath key: \(key)")
_layoutAttributes[key] = attributes
if transposed {
yOffset += itemSize.height
yOffset += self.verticalInset
}else {
xOffset += itemSize.width
xOffset += self.horizontalInset
}
}
//After going through all items in section, check if current section is not the last section
if !(section == numberOfSections) {
if transposed {
//Increase xOffset for correct horizontal placement
xOffset += self.horizontalInset
xOffset += self.itemWidth
//Reset yOffset for correct spacing between cells
yOffset = self.verticalInset
}else {
//Increase yOffset for correct vertical placement
yOffset += self.verticalInset
yOffset += self.itemHeight
//Reset xOffset for correct spacing between cells
xOffset = self.horizontalInset
}
print("Finished adding items in section \(section), xOffset: \(xOffset), yOffset: \(yOffset)")
}else{
if transposed {
yOffset = self.verticalInset
}else {
xOffset = self.horizontalInset
}
}
}
if transposed {
xOffset += self.itemWidth
}else {
yOffset += self.itemHeight
}
//Calculate size of content based on largest number of items in the sections
print("xOffset: \(xOffset), yOffset: \(yOffset)")
if transposed {
_contentSize = CGSize(width: xOffset + self.horizontalInset, height: (CGFloat(largestNumberOfItems) * (itemHeight + yOffset)))
}else {
_contentSize = CGSize(width: (CGFloat(largestNumberOfItems) * (itemWidth + xOffset)), height: yOffset + self.verticalInset)
}
print("content size :\(_contentSize)")
}
func changeItemSize(_ newWidth: CGFloat, newHeight: CGFloat){
self.itemHeight = newHeight
self.itemWidth = newWidth
}
func changeInset(_ newHorizontalInset: CGFloat, newVerticalInset: CGFloat){
self.horizontalInset = newHorizontalInset
self.verticalInset = newVerticalInset
}
func layoutKeyForIndexPath(_ indexPath : IndexPath) -> String {
return "\(indexPath.section)_\(indexPath.row)"
}
func layoutKeyForHeaderAtIndexPath(_ indexPath : IndexPath) -> String {
return "s_\(indexPath.section)_\(indexPath.row)"
}
override var collectionViewContentSize : CGSize {
return _contentSize
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let headerKey = layoutKeyForIndexPath(indexPath)
return _layoutAttributes[headerKey]
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let key = layoutKeyForIndexPath(indexPath)
return _layoutAttributes[key]
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let predicate = NSPredicate { [unowned self] (evaluatedObject, bindings) -> Bool in
let layoutAttribute = self._layoutAttributes[evaluatedObject as! String]
return rect.intersects(layoutAttribute!.frame)
}
let dict = _layoutAttributes as NSDictionary
let keys = dict.allKeys as NSArray
let matchingKeys = keys.filtered(using: predicate)
return dict.objects(forKeys: matchingKeys, notFoundMarker: NSNull()) as? [UICollectionViewLayoutAttributes]
}
}
So it turns out that the cause of the crash was me forgetting that the number of items returned by the collectionview's numberOfItems(inSection) was not zero-based. I didn't think to look there because the horizontal layout handled this inconsistency fine for some unknown reason.

UICollectionViewFlowLayout with centered cells

I'm trying to implement a horizontal aligned CollectionView with centered cells and I'm using a custom UICollectionViewFlowLayout. I found several solutions with UICollectionViewDelegateFlowLayout but the delegate is not called, if you use a custom UICollectionViewFlowLayout.
Here is my UICollectionViewFlowLayout. Any ideas what to do?
class MyFlowLayout: UICollectionViewFlowLayout {
var numberOfRows = 1
var cellPadding: CGFloat = 15.0
var availableSize = CGSize.zero
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentWidth: CGFloat = 0.0
fileprivate var contentHeight: CGFloat {
var collectionViewHeight = collectionView!.bounds.height
if availableSize.height > 0{
collectionViewHeight = availableSize.height
}
let insets = collectionView!.contentInset
return collectionViewHeight - (insets.top + insets.bottom)
}
func reInit(_ newSize: CGSize){
availableSize = newSize
cache.removeAll()
}
override func prepare() {
if cache.isEmpty {
let rowHeight = contentHeight / CGFloat(numberOfRows)
var yOffset = [CGFloat]()
for row in 0 ..< numberOfRows {
yOffset.append(CGFloat(row) * rowHeight )
}
var row = 0
var xOffset = [CGFloat](repeating: 0, count: numberOfRows)
for item in 0 ..< collectionView!.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let height = rowHeight
let width = CGFloat(height / 1.27)
let frame = CGRect(x: xOffset[row], y: yOffset[row], width: width, height: height)
let insetFrame = frame.insetBy(dx: cellPadding * 2, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentWidth = max(contentWidth, frame.maxX)
xOffset[row] = xOffset[row] + width
row = row >= (numberOfRows - 1) ? 0 : (row + 1)
}
}
}
override var collectionViewContentSize : CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for attributes in cache {
if attributes.frame.intersects(rect) {
layoutAttributes.append(attributes)
}
}
return layoutAttributes
}
}
The layout is connected this way:
collectionView.collectionViewLayout = MyFlowLayout()
collectionView.register(MyCell.self, forCellWithReuseIdentifier: MyCell.identifier)
collectionView.dataSource = self
Prepare in MyFlowLayout is called.

Resources