How To Add Vertical Padding to Collection View

In Swift we have UICollectionView, in Android there is RecyclerView. However, on Android, there is an option clipToPadding that adds padding to the RecyclerView and this padding doesn’t move on the scroll. In Swift, we don’t have this option but it’s possible to simulate it.

The result of this post is that when you scroll CollectionView the vertical padding is still remaining.

In the video, the whole CollectionView has a black background, and brown are cells.

Ok, if you’re still here, it means you want to know how to do it.

First let’s create a ViewController with CollectionView, something you did many times.

class CollectionViewController: UIViewController {
    
let collectionView: UICollectionView = {
    
    let layout = UICollectionViewFlowLayout()
    
    layout.scrollDirection = .vertical
    
    let collection = UICollectionView(frame: CGRect(x: 50, y: 50, width: UIScreen.main.bounds.width100, height: UIScreen.main.bounds.height100), collectionViewLayout: layout)
    
    return collection
    }()

    
let items = (1…30)

    
let CELL_ID = “Cell”

    
override func viewDidLoad() {
    
    super.viewDidLoad()
    
    view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
    
    view.addSubview(collectionView)
    
    collectionView.register(CollectionViewControllerCell.self, forCellWithReuseIdentifier: CELL_ID)
    
    collectionView.dataSource = self
    
    collectionView.delegate = self
  
  }
}

And add two extensions.

extension CollectionViewController: UICollectionViewDelegateFlowLayout {
    
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
    return CGSize(width: collectionView.bounds.width32, height: 48)
    }
}

extension CollectionViewController: UICollectionViewDataSource {
    
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    
    return items.count
}
    
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CELL_ID, for: indexPath) as! CollectionViewControllerCell
    
    return cell
    }
}

The last part is the custom Cell. For the purpose of this example, it’s in the same file.

class CollectionViewControllerCell: UICollectionViewCell {
    
override init(frame: CGRect) {
        
super.init(frame: frame)
        
self.backgroundColor = #colorLiteral(red: 0.5058823824, green: 0.3372549117, blue: 0.06666667014, alpha: 1)
    }
    
required init?(coder aDecoder: NSCoder) {
    
    super.init(coder: aDecoder)
    }
}

The result is as you expected, there isn’t any vertical padding. There is some kind of padding because each item’s width is equal to the collection view width minus 32.

Even if you use content inset it doesn’t work when you scroll.

Adding the line below won’t fix it.

collectionView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)

Solution

It’s a little workaround because it uses footer and header cells, they are pinned to view and they have the same color as the collection view. It all together gives the wanted results.

First, create a custom view for the header and footer.

class CollectionViewControllerHeaderFooterCell: UICollectionReusableView {
    
override init(frame: CGRect) {
    
    super.init(frame: frame)
    
    self.backgroundColor = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
    }

    
required init?(coder aDecoder: NSCoder) {
    
    super.init(coder: aDecoder)
    }
}

For now, it’s red to see where it’s placed.

Back to collectionView and add those two lines.

layout.sectionFootersPinToVisibleBounds = true
layout.
sectionHeadersPinToVisibleBounds = true

It prevents scrolling these sections.

Add id for header and footer. It can be next to CELL_ID.

let HEADER_FOOTER_ID = “HeaderFooterId”

Register header and footer for collection view.

collectionView.register(CollectionViewControllerHeaderFooterCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HEADER_FOOTER_ID)
collectionView.register(CollectionViewControllerHeaderFooterCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: HEADER_FOOTER_ID)

Add it next to Cell registration.

Inside the extension with UICollectionViewDelegateFlowLayout add two delegates for size, here you can adjust how big the top and bottom padding should be.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    
return CGSize(width: collectionView.frame.width, height: 16.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
    
return CGSize(width: collectionView.frame.width, height: 16.0)
}

Inside the second extension add.

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    
switch kind {
    
    case UICollectionView.elementKindSectionHeader:
    
    let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HEADER_FOOTER_ID, for: indexPath)
    
    return headerView

    
    case UICollectionView.elementKindSectionFooter:
    
    let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HEADER_FOOTER_ID, for: indexPath)
    
    return footerView
    
default:
    
    return UICollectionReusableView()
    }
}

Result

Notice, when you scroll, the red cells don’t move. Now you can change the color to black.

// Inside CollectionViewControllerHeaderFooterCell
self
.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)