How to center align the cells of a UICollectionView in Swift3.0?

Here's a recent solution, works for Swift 5.0

extension YourViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        let itemWidth = 80
        let spacingWidth = 4
        let numberOfItems = collectionView.numberOfItems(inSection: section)
        let cellSpacingWidth = numberOfItem * spacingWidth
        let totalCellWidth = numberOfItems * itemWidth + cellSpacingWidth
        let inset = (collectionView.layer.frame.size.width - CGFloat(totalCellWidth)) / 2
        return UIEdgeInsets(top: 5, left: inset, bottom: 5, right: inset)
    }
}

While rottenoats answer is great, it has an extra spacing bug and doesn't use much of the available swift 3 syntax. I've fixed that and reduced the number of dependencies to local variables.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

    // Make sure that the number of items is worth the computing effort.
    guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout,
        let dataSourceCount = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section),
        dataSourceCount > 0 else {
            return .zero
    }


    let cellCount = CGFloat(dataSourceCount)
    let itemSpacing = flowLayout.minimumInteritemSpacing
    let cellWidth = flowLayout.itemSize.width + itemSpacing
    var insets = flowLayout.sectionInset


    // Make sure to remove the last item spacing or it will
    // miscalculate the actual total width.
    let totalCellWidth = (cellWidth * cellCount) - itemSpacing
    let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right


    // If the number of cells that exist take up less room than the
    // collection view width, then center the content with the appropriate insets.
    // Otherwise return the default layout inset.
    guard totalCellWidth < contentWidth else {
        return insets
    }


    // Calculate the right amount of padding to center the cells.
    let padding = (contentWidth - totalCellWidth) / 2.0
    insets.left = padding
    insets.right = padding
    return insets
}

N.B.: This snippet only works for an horizontal scrolling but can easily be adjusted.


This ended up being the solution I used. Read the code comments for a better understanding. Swift 5

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

    //Where elements_count is the count of all your items in that
    //Collection view...
    let cellCount = CGFloat(elements_count)

    //If the cell count is zero, there is no point in calculating anything.
    if cellCount > 0 {
        let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
        let cellWidth = flowLayout.itemSize.width + flowLayout.minimumInteritemSpacing

        //20.00 was just extra spacing I wanted to add to my cell.
        let totalCellWidth = cellWidth*cellCount + 20.00 * (cellCount-1)
        let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right

        if (totalCellWidth < contentWidth) {
            //If the number of cells that exists take up less room than the
            //collection view width... then there is an actual point to centering them.

            //Calculate the right amount of padding to center the cells.
            let padding = (contentWidth - totalCellWidth) / 2.0
            return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
        } else {
            //Pretty much if the number of cells that exist take up
            //more room than the actual collectionView width, there is no
            // point in trying to center them. So we leave the default behavior.
            return UIEdgeInsets(top: 0, left: 40, bottom: 0, right: 40)
        }
    }
    return UIEdgeInsets.zero
}

Slight adaptation of @rottenoats answer. This is more generic.

Most importantly remember to make your view controller conform to the UICollectionViewDelegateFlowLayout protocol.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

    guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else {
        return .zero
    }

    let cellCount = CGFloat(collectionView.numberOfItems(inSection: section))

    if cellCount > 0 {
        let cellWidth = flowLayout.itemSize.width + flowLayout.minimumInteritemSpacing

        let totalCellWidth = cellWidth * cellCount
        let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right - flowLayout.headerReferenceSize.width - flowLayout.footerReferenceSize.width

        if (totalCellWidth < contentWidth) {
            let padding = (contentWidth - totalCellWidth + flowLayout.minimumInteritemSpacing) / 2.0
            return UIEdgeInsetsMake(0, padding, 0, 0)
        }
    }

    return .zero
}