Search code examples
iosswiftxcodeuicollectionviewspacing

UICollectionView Top spacing


i have a Problem with my UICollectionView inside my Swift Xcode Project enter image description here I checked all insets and margins, I tried the ViewController's: "Adjust Scroll View Insets" and lots of other stuff, but none of them did change this in any way.

i want to reduce / remove that top spacing above the first row of cells in my collectionView

I created a sample project to find a solution/fix for this issue, but didn't had any luck yet

This seems to be a issue when using a Collection View inserted via Storyboard /Interface builder, because this does not happen if I add the collectionView on the code side

Here's my View Controller's Code:

import UIKit

struct CVData: Hashable {
    var name: String
    var value: String
}

class ViewController: UIViewController {
    
    @IBOutlet weak var myCollectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource<Section, CVData>!
    var cvDataList: [CVData] = []
    enum Section {
        case main
    }
    var CVDataLabels: [String] = ["Label1:","Label2:","Label3:","Label4:","Label5:","Label6:","Label7:","Label8:"]
    var CVDataValues: [String] = []
    var snapshot: NSDiffableDataSourceSnapshot<Section, CVData>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureCollectionView()
        buildData()
        // Do any additional setup after loading the view.
    }

    func configureCollectionView() {
        
        let layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
        myCollectionView.collectionViewLayout = listLayout
        myCollectionView.isScrollEnabled = false
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, CVData> { (cell, indexPath, item) in

            var content = UIListContentConfiguration.cell()
            content.text = item.name
            content.textProperties.font.withSize(8.0)
            content.textProperties.font = UIFont.preferredFont(forTextStyle: .body)
            content.secondaryText = item.value
            content.prefersSideBySideTextAndSecondaryText = true
            content.textProperties.adjustsFontSizeToFitWidth = false
            cell.contentConfiguration = content
        }
        
        dataSource = UICollectionViewDiffableDataSource<Section, CVData>(collectionView: myCollectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: CVData) -> UICollectionViewCell? in
            
            // Dequeue reusable cell using cell registration (Reuse identifier no longer needed)
            let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
                                                                    for: indexPath,
                                                                    item: identifier)
            // Configure cell appearance
            cell.accessories = [.disclosureIndicator()]
            
            return cell
        
    }
    }


    func buildData() {
        
        let cvDataList = [
            CVData(name: self.CVDataLabels[0], value: "value1"),
            CVData(name: self.CVDataLabels[1], value: "value2"),
            CVData(name: self.CVDataLabels[2], value: "value3"),
            CVData(name: self.CVDataLabels[3], value: "value4"),
            CVData(name: self.CVDataLabels[4], value: "value5"),
            CVData(name: self.CVDataLabels[5], value: "value6"),
            CVData(name: self.CVDataLabels[6], value: "value6"), //added 20210510
            CVData(name: self.CVDataLabels[7], value: "value7")
        ]
        
        // Create a snapshot that define the current state of data source's data
        self.snapshot = NSDiffableDataSourceSnapshot<Section, CVData>()
        self.snapshot.appendSections([.main])
        self.snapshot.appendItems(cvDataList, toSection: .main)

        // Display data in the collection view by applying the snapshot to data source
        self.dataSource.apply(self.snapshot, animatingDifferences: false)
    }
}

Here's the Size inspector settings from that UICOllectionView enter image description here

Any help'd be greatly appreciated 👍


Solution

  • I haven't worked with UICollectionViewCompositionalLayout.list, so this is just from some quick research and experimentation...

    The default .headerMode for UICollectionLayoutListConfiguration is .none ... when using .insetGrouped that results in the "extra space" we're seeing.

    Curiously, there is also a headerTopPadding property, which we might expect to be of help here. It's default is 0 ... but that appears to only be applied if there IS a header view.

    So, if we set .headerMode = .supplementary and return an empty UICollectionReusableView for the header, we can get rid of the extra space:

    enter image description here

    Now, the top space is the same as the default left/right padding.

    Here's what I used for the header view:

    class EmptyHeaderView : UICollectionReusableView {
        static let reuseIdentifier:String = "emptyHeaderView"
    }
    

    and three edits to your configureCollectionView() func:

    func configureCollectionView() {
        
        var layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        
        // 1
        // we're going to supply an "empty" header supplementary view
        layoutConfig.headerMode = .supplementary
        
        let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
        
        myCollectionView.collectionViewLayout = listLayout
        myCollectionView.isScrollEnabled = false
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, CVData> { (cell, indexPath, item) in
            
            var content = UIListContentConfiguration.cell()
            content.text = item.name
            content.textProperties.font.withSize(8.0)
            content.textProperties.font = UIFont.preferredFont(forTextStyle: .body)
            content.secondaryText = item.value
            content.prefersSideBySideTextAndSecondaryText = true
            content.textProperties.adjustsFontSizeToFitWidth = false
            cell.contentConfiguration = content
        }
        
        dataSource = UICollectionViewDiffableDataSource<Section, CVData>(collectionView: myCollectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: CVData) -> UICollectionViewCell? in
            
            // Dequeue reusable cell using cell registration (Reuse identifier no longer needed)
            let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
                                                                    for: indexPath,
                                                                    item: identifier)
            // Configure cell appearance
            cell.accessories = [.disclosureIndicator()]
            
            return cell
            
        }
        
        // 2
        // register a reusable supplementary view for the header
        myCollectionView.register(EmptyHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: EmptyHeaderView.reuseIdentifier)
    
        // 3
        // provider for header suppleentary view
        dataSource.supplementaryViewProvider = { (collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? in
            if kind == UICollectionView.elementKindSectionHeader {
                if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: EmptyHeaderView.reuseIdentifier, for: indexPath) as? EmptyHeaderView {
                    return headerView
                }
            }
            fatalError("Something's wrong with the setup...")
        }
    }