What is Sticky Rows and Columns with Collection view In Swift?
UICollectionView implements a collection view that has sticky rows and columns and supports both vertical and horizontal scrolling using Swift. Using StickyLayout quick and simple. First import StickyLayout. You then have the option of creating an instance of StickyConfig, where you can specify which rows/columns you want to be sticky. Create an instance of StickyLayout with your StickyConfig as a parameter, and add it to your UICollectionView.
Let execute Sticky Rows and Columns with Collection view
Step 1. create a collection view in your main controller
and define collection view in your controller for example
1 2 3 4 5 |
@IBOutlet weak var gridCollectionView: UICollectionView! { didSet { gridCollectionView.bounces = false } } |
create a grid layout of your collection view as StickyGridCollectionViewLayout
1 2 3 4 5 6 |
@IBOutlet weak var gridLayout: StickyGridCollectionViewLayout! { didSet { gridLayout.stickyRowsCount = 1 gridLayout.stickyColumnsCount = 1 } } |
For now, let’s ignore the compiler’s warnings to better understand the logic flow.
- Remove all previously calculated attributes as they might be no longer relevant, and initialize offset variables.
- Iterate over all rows within a grid. When working with a grid, it is easier to think about cells in terms of rows in columns rather than items and sections. For this purpose, we will implement
rowsCount
andcolumnsCount(in:)
later. - Make preparations before we calculate attributes for the next row. Each row must begin with 0 positions, thus we need to reset
xOffset
. Attributes of each row are stored in an array. - Iterate over all columns within a row.
- Calculate a frame of a cell. We are accumulating
xOffset
andyOffset
to position the cell correctly within the grid. The size is received from the new methodsize(forRow:,column:)
that will be implemented later. - Lastly, the row attributes are appended to
allAttributes
that contains attributes for the whole grid.
Collection view data source and delegate methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func numberOfSections(in collectionView: UICollectionView) -> Int { return 20 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.reuseID, for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() } cell.titleLabel.text = "test \(indexPath)" cell.backgroundColor = gridLayout.isItemSticky(at: indexPath) ? .groupTableViewBackground : .white return cell } |
Define layout size of your collection view cell
1 2 3 |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 100, height: 100) } |
Define row count in the layout class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class StickyGridCollectionViewLayout: UICollectionViewFlowLayout { var stickyRowsCount = 0 { didSet { invalidateLayout() } } var stickyColumnsCount = 0 { didSet { invalidateLayout() } } private var allAttributes: [[UICollectionViewLayoutAttributes]] = [] private var contentSize = CGSize.zero func isItemSticky(at indexPath: IndexPath) -> Bool { return indexPath.item < stickyColumnsCount || indexPath.section < stickyRowsCount } |
Collection view flow layout methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
override var collectionViewContentSize: CGSize { return contentSize } override func prepare() { setupAttributes() updateStickyItemsPositions() let lastItemFrame = allAttributes.last?.last?.frame ?? .zero contentSize = CGSize(width: lastItemFrame.maxX, height: lastItemFrame.maxY) } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for rowAttrs in allAttributes { for itemAttrs in rowAttrs where rect.intersects(itemAttrs.frame) { layoutAttributes.append(itemAttrs) } } return layoutAttributes } override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } |
Put size of your Collection view Cell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private var rowsCount: Int { return collectionView!.numberOfSections } private func columnsCount(in row: Int) -> Int { return collectionView!.numberOfItems(inSection: row) } private func size(forRow row: Int, column: Int) -> CGSize { guard let delegate = collectionView?.delegate as? UICollectionViewDelegateFlowLayout, let size = delegate.collectionView?(collectionView!, layout: self, sizeForItemAt: IndexPath(row: row, column: column)) else { assertionFailure("Implement collectionView(_,layout:,sizeForItemAt: in UICollectionViewDelegateFlowLayout") return .zero } return size } |
You can see the output below
For more detail please click here
I hope this blog helped with any query please leave a comment to us and for more blogs please click here.