10

I'm looking for a way to remove top section padding in my SwiftUI List

With iOS 15, we can do UITableView.appearance().sectionHeaderTopPadding = 0

However, with iOS 16, List has been reimplemented with UICollectionView and I couldn't find a way to remove the section header top padding

Here is sample code

import SwiftUI

struct TaskRow: View {
    var body: some View {
        Text("Task data goes here")
    }
}

struct HeaderText: View {
    var text:String
    var body: some View {
        Text(text)
            .font(.system(.title3))
            .fontWeight(.bold)
            .foregroundColor(.primary)
    }
}

struct ListWithSections: View {
    init() {
        if #available(iOS 15.0, *) {
            UITableView.appearance().sectionHeaderTopPadding = 0
        } else {
            // Fallback on earlier versions
        }
    }
    
    var body: some View {
        if #available(iOS 16.0, *) {
            List {
                Section(header: HeaderText(text: "Section 1")) {
                    TaskRow()
                    TaskRow()
                    TaskRow()
                }
                
                Section(header: HeaderText(text: "Section 2")) {
                    TaskRow()
                    TaskRow()
                    TaskRow()
                }
                
            }
            .scrollContentBackground(.hidden)
            .background(Color.gray)
            .listStyle(.plain)
        } else {
            List {
                Section(header: HeaderText(text: "Section 1")) {
                    TaskRow()
                    TaskRow()
                    TaskRow()
                }
                
                Section(header: HeaderText(text: "Section 2")) {
                    TaskRow()
                    TaskRow()
                    TaskRow()
                }
                
            }
            .background(Color.gray)
            .listStyle(.plain)
        }
    }
}

Images for comparing between iOS 16 and iOS 15

ios16 and ios15

Any way we can achieve it with List Plain Style?

Update: Using ListRowInset as @Yrb suggested will give almost same UI. However when we scroll, we have minor difference

Here what scroll in ios15 looks like (what I want to achieve in ios16) enter image description here

Here what scroll in ios16 looks like (with listRowInset as @Yrb suggested) enter image description here

Thanks in advance.

Best regards.

Ryan_Chau
  • 233
  • 2
  • 19
  • If the answer below helped you, please mark it as the solution. – Bart van Kuik Sep 27 '22 at 13:02
  • 2
    @BartvanKuik Sorry but the answer from Yrb doesn't answer as my expectation for this question. The first version of my question before Yrb answered already stated about the behavior I wanted in iOS 16 is same as `With iOS 15, we can do UITableView.appearance().sectionHeaderTopPadding = 0` . Therefore, Yrb's answer doesn't match my expected behavior – Ryan_Chau Sep 28 '22 at 02:21

1 Answers1

1

SwiftUI has had a native implementation of this since iOS 13, but no one talked about it because you could reach under and set the UITableView.appearance(). What you are dealing with in SwiftUI is listRowInsets and the padding that Apple adds to the section header. Essentially what you do is take control of all of the insets. This is a double edged sword as you now control it all, you can't just set one and leave the rest as a default.

One thing you will find is that the defaults have changed from iOS 15 to iOS 16, so you can't use the same numbers. In iOS 16, you will also need to set the defaultMinListHeaderHeight in .environment.

I also extracted your section to save the repetition.

struct ListSecHeaderHeightView: View {
    var body: some View {
        List {
            Section(header: SectionView(text: "Section 1")) {
                Text("TaskRow")
                Text("TaskRow")
                Text("TaskRow")
            }
            Section(header: SectionView(text: "Section 2")) {
                Text("TaskRow")
                Text("TaskRow")
                Text("TaskRow")
            }
        }
        .scrollContentBackground(.hidden)
        .environment(\.defaultMinListHeaderHeight, 0) // Only necessary in iOS 16. For 15, use 30
        .background(Color.gray)
        .listStyle(.plain)
    }
}

struct SectionView: View {
    let text: String
    var body: some View {
        HStack {
            Text(text)
                .font(.system(.title3))
                .fontWeight(.bold)
                .foregroundColor(.primary)
        }
        // Set it here.
        // iOS 15:
        // The top goes to -8 to cancel out the padding.
        // The leading goes to 16 as you had the section in line with the rows.
        // iOS 16:
        // The top goes to -20.
        // The leading goes to 20 as you had the section in line with the rows.
        .listRowInsets(EdgeInsets(top: -8, leading: 16, bottom: 0, trailing: 0))
    }
}

Lastly, please post Minimum Reproducible Examples. We should be able to run your code as posted.

Yrb
  • 8,103
  • 2
  • 14
  • 44
  • Thanks for your reply I edited my answer to add minimum reproducible code – Ryan_Chau Aug 28 '22 at 23:36
  • 1
    I tried your suggestion, it looks good However when we scroll, the sticky header is still a bit strange in ios16 Is there anyway to customize that to be same as ios 15 as well ? – Ryan_Chau Aug 28 '22 at 23:44
  • I added some picture to clarify my point about scroll in the question – Ryan_Chau Aug 29 '22 at 00:40
  • That should be asked as a new question. This allows people who have the same question as you to actually be able to find it on SO. – Yrb Aug 30 '22 at 00:46
  • Setting negative padding is not a good idea. The section headers. fails at the scroll. – kelalaka May 12 '23 at 16:41