Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove section header top padding in SwiftUI Plain List with iOS16

When we use SwiftUI List component and need a section header, top padding will appear on the header of each section.

Under iOS15, we use UITableView.appearance().sectionHeaderTopPadding = 0 to solve this problem. Check out attachmentiOS15&Xcode14 build screenshot.

But in iOS16, this setting doesn't seem to work. Check out attachmentiOS16&Xcode14 build screenshot, when scrolling: iOS16&Xcode14 build scroll.

So, my problem is how to remove section header top padding in iOS16, and when scrolling List, its section header will be pinned, just like attachment iOS15&Xcode14 build scroll.

Since iOS16, SwiftUI List component seems to use UICollectionView instead of UITableView, so I tried to solve this problem by setting the section top padding of UICollectionView globally. Unfortunately, I didn't find a way to set the section top padding of UICollectionView.

My code is below👇.

//
//  ContentView.swift
//  ListIniOS16
//
//  Created by Eric on 2022/07/23.
//

import SwiftUI

struct ContentView: View {
    @State private var collections: [ListData] = ListData.collection

    init() {
        let appBarTheme = UINavigationBarAppearance()
        appBarTheme.configureWithOpaqueBackground()
        appBarTheme.backgroundColor = UIColor(.gray)
        UINavigationBar.appearance().standardAppearance = appBarTheme
        UINavigationBar.appearance().scrollEdgeAppearance = appBarTheme
        if #available(iOS 15.0, *) {
            // can fix sectionHeaderTopPadding issue in iOS15,
            // but in iOS16 it doesn't work.
            UITableView.appearance().sectionHeaderTopPadding = 0
        }
    }
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(collections) { item in
                        Section(header: SectionHeader(title: item.group)) {
                            ForEach(item.rows) { row in
                                Text(row.name)
                            }
                        }
                        .listRowBackground(Color.gray.opacity(0.3))
                    }
                }
                .listStyle(.plain)
            }
            .navigationTitle("ListSectionHeader")
            .navigationBarTitleDisplayMode(.inline)
        }
    }

    struct SectionHeader: View {
        let title: String

        var body: some View {
            ZStack(alignment: .leading) {
                Color(.lightGray)
                    .frame(maxWidth: .infinity)

                Text(title)
                    .font(.system(size: 16, weight: .bold))
                    .foregroundColor(.primary)
                    .padding(EdgeInsets(top: 10, leading: 18, bottom: 10, trailing: 18))
            }
            .listRowInsets(EdgeInsets())
        }
    }
}

struct ListData: Codable, Identifiable {
    var id = UUID()
    let group: String
    let rows: [Row]
}

extension ListData {
    static let collection: [ListData] = [.a, .b, .c]

    static let a = ListData(group: "A", rows: [.row1, .row2, .row3])

    static let b = ListData(group: "B", rows: [.row1, .row2, .row3, .row1, .row2, .row3])

    static let c = ListData(group: "C", rows: [.row1, .row2, .row3, .row1, .row2, .row3, .row1, .row2, .row3])
}

struct Row: Codable, Equatable, Identifiable {
    var id = UUID()
    let name: String
}

extension Row {
    static let row1 = Row(name: "Row1")
    static let row2 = Row(name: "Row2")
    static let row3 = Row(name: "Row3")
}

Many thanks.

like image 641
Eric Avatar asked Sep 12 '25 09:09

Eric


1 Answers

You can remove the top padding from section headers in UICollectionViews like this:

var layoutConfig = UICollectionLayoutListConfiguration(appearance: .plain)
layoutConfig.headerMode = .supplementary
layoutConfig.headerTopPadding = 0
let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
UICollectionView.appearance().collectionViewLayout = listLayout

Edit: Unfortunately I just saw that this overrides certain SwiftUI modifiers like .listRowSeparator() or .onDelete().

like image 80
crazycrayfish Avatar answered Sep 16 '25 03:09

crazycrayfish