UICollectionView remove section breaks with UICollectionViewFlowLayout

I have a dataset that is divided into multiple sections, however, I’d like to display this in a collectionView without breaks between sections. Here’s an illustration of what I want to achieve:

Instead of:
0-0 0-1 0-2
0-3
1-0 1-1
2-0
3-0

I want:
0-0 0-1 0-2
0-3 1-0 1-1
2-0 3-0

I realize the solution likely lies with a custom UICollectionViewLayout subclass, but I’m not sure how to achieve something like this.

  • Could not insert new outlet connection error
  • iOS 11 large-title navigation bar not collapsing
  • iPad title bars. Navbars or toolbars?
  • Undo with multitouch drawing in iOS
  • How come my drawing code keeps resulting in fuzzy shapes?
  • Relationship between UIVIew and CALayer regarding background image on iOS
  • Thanks

    Solutions Collect From Internet About “UICollectionView remove section breaks with UICollectionViewFlowLayout”

    You are correct that you need to subclass UICollectionViewLayout.
    The essence to understand before starting is that you need to calculate at least position and size for every cell in the collection view. UICollectionViewLayout is just a structured way to provide that information. You get the structure, but you have to provide everything else yourself.

    There are 4 methods you need to override:

    • prepare
    • invalidateLayout
    • layoutAttributesForItemAtIndexPath
    • layoutAttributesForElementsInRect

    One trick is to cache the layout attributes in a lookup table (dictionary):

    var cachedItemAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
    

    In prepare, you calculate the layout attributes for each indexPath in your collectionView:

    override func prepare() {
        super.prepare()
        calculateAttributes()
    }
    

    In invalidateLayout you reset the cached layout attributes and recalculate them:

    override func invalidateLayout() {
        super.invalidateLayout()
        cachedItemAttributes = [:]
        calculateAttributes()
    } 
    

    In layoutAttributesForItemAtIndexPath you use the lookup table to return the right layout attributes:

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cachedItemAttributes[indexPath]
    }
    

    In layoutAttributesForElementsInRect you filter your lookup table for the elements within the specified rect:

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return cachedItemAttributes.filter { rect.intersects($0.value.frame) }.map { $0.value }
    }
    

    The final piece of the puzzle is the actual calculation of the layout attributes. Here I will provide only pseudo-code:

    func calculateAttributes() {
        // For each indexpath (you can get this from the collectionView property using numberOfSections and numberOfItems:inSection )
        // calculate the frame, i.e the origin point and size of each cell in your collectionView and set it with UICollectionViewLayoutAttributes.frame
        // There are many other properties on UICollectionViewLayoutAttributes that you can tweak for your layout, but frame is a good starting point from which you can start experimenting.
        // Add the layout attributes to the lookup table
        // end loop
    }
    

    To answer your question, here is pseudo-code to calculate the position of each cell:

    // If width of cell + current width of row + spacing, insets and margins exceeds the available width
    // move to next row.
    // else
    // cell origin.x = current width of row + interitem spacing
    // cell origin.y = number of rows * (row height + spacing)
    // endif
    

    If you need your custom layout to be configurable, then either use UICollectionViewDelegateFlowLayout if the available signatures are sufficient, or define your own that inherits from UICollectionViewDelegateFlowLayout or UICollectionViewDelegate. Because your protocol inherits from UICollectionViewDelegateFlowLayout, which itself inherits from UICollectionViewDelegate, you can set it directly as the collectionView delegate in your viewcontroller. In your custom collection view layout you just need to cast the delegate from UICollectionViewDelegate to your custom protocol to use it. Remember to handle cases where the casting fails or where the protocol methods are not implemented by the delegate.