Mitigating overlapping text for sticky section headers for a plain List in iOS 26

When preparing SwiftUI code that uses List with .listStyle(.plain) for iOS 26, the by-default sticky section headers combined with the new translucent top-bars often causes unpleasantly overlapping text:

Two questions here:

Is there a modifier to make section headers non-sticky?

  • This would be helpful for cases where the translucent bar is a good fit and the section titles don't need to be sticky/pinned.
  • I found .listStyle(.grouped) can be an alternative in some cases, but this adds a gray background / additional padding to the section titles.

Is there a way to get a blurry material behind the section headers when they are sticking to the top bar?

  • This would be good for cases where the section header is important content-wise (like in the two-column example above or for a data list categorized using sections that should be always visible as a point of reference)
  • I found the scroll edge effects and .scrollEdgeEffectStyle(.hard, for: .top) does the trick for the top bar but doesn't affect attached sticky section headers (maybe it should?). Also I played around with .toolbarBackground(...) but this didn't do anything useful for a nav bar in my experiments.

Is there a modifier to make section headers non-sticky?

You can use the automatic listStyle .listStyle(.automatic) or opt for a lazy stack instead and apply your decorators. For examples, refer to Grouping data with lazy stack views.

Is there a way to get a blurry material behind the section headers when they are sticking to the top bar?

You can provide a background for your section header. However you can't provide a material for the scrollEdgeEffectStyle apart from either hard or soft ScrollEdgeEffectStyle.

Can you give safeAreaBar(edge:alignment:spacing:content:) a try and disable the scroll edge effect using scrollEdgeEffectHidden. That would allow you provide a view as a custom bar.

Is there a modifier to make section headers non-sticky?

You can use the automatic .listStyle(.automatic) or opt for a lazy stack instead and apply your decorators. For examples, refer to Grouping data with lazy stack views.

.listStyle(.automatic) results in in .insetGrouped, so that's a different look.

I tried the LazyVStack approach and this worked well for a screen that was pretty custom-styled anyway.

Side note: LazyVStack made me a little upset by applying the spacing not only between rows, but also after the section header; requiring dancing around with the spacing and paddings to get the correct look; I wished for a separation of "spacing between sections", "spacing between rows" and "spacing between [section header/rows/section footer]".

But I'm wondering if this is a well-rounded approach for Views that just want a plain styled data list. It's certainly more feasible with the delicate separators-going-all-the-way-through-to-the-edge-of-the-screen gone in iOS 26, but still, custom-rebuilding all the Views with details like spacings and font styling and separators; just to get non-sticky headers?

Even with List seemingly stuck in its past as UITable/CollectionViewController wrapper, I'm keeping my fingers crossed that something like List and .listStyle(.plain) might get a come-back. Imo it would be worth having a View that provides a plain minimal default styling for plain simple data lists with convenience like configurable pinnedHeaders etc.

You can provide a background for your section header. However you can't provide a material for the scrollEdgeEffectStyle apart from either hard or soft ScrollEdgeEffectStyle. Can you give safeAreaBar(edge:alignment:spacing:content:) a try and disable the scroll edge effect using scrollEdgeEffectHidden. That would allow you provide a view as a custom bar.

To clarify, I'm looking for a way to get the same blurry material behind the top bar and the pinned column headers. So I was hoping there would be a way to extend the scrollEdgeEffectStyle to the pinned headers or to add my own custom view (instead of pinned headers) into/below the top bar (so that it then gets the same material).

I don't care what material it is exactly, I just want it to go nicely with the iOS 26 look. In this example I also don't care if it's a pinned header or an extra to the top bar, as it's only one header for the whole view. It would be nice to have a general solution for pinned section headers though.

Side note: The Calendar app does something similar to the "extra view in the top bar" to get a the week days above the calendar:

When I use .background(Material.regular) in a .safeAreaBar, the top bar always gets an opaque light gray background (beta 4), so that doesn't seem to do the trick (using List here, but it's the same with LazyVStack):

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List {
                ForEach(1...100, id: \.self) { idx in
                    Text("Row \(idx)")
                }
            }
            .listStyle(.plain)
            .navigationTitle("Example")
            .navigationBarTitleDisplayMode(.inline)
            .safeAreaBar(edge: .top) {
                Text("Header")
                    .font(.subheadline.bold())
                    .foregroundStyle(.secondary)
                    .padding(.horizontal)
                    .padding(.vertical, 4)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(Material.regular)
            }
            //.scrollEdgeEffectStyle(.hard, for: [.top])
            .scrollEdgeEffectHidden()
        }
    }
}

👍 .safeAreaBar() now works together with .scrollEdgeEffectStyle(.hard, for: .top) out of the box in Beta 5:

struct SafeAreaBarExample: View {
    var body: some View {
        NavigationStack {
            List {
                Text("Hello")
            }
            .listStyle(.plain)
            .navigationTitle("Example")
            .navigationBarTitleDisplayMode(.inline)
            .scrollEdgeEffectStyle(.hard, for: .top)
            .safeAreaBar(edge: .top, alignment: .leading) {
                Text("Safe Area Bar")
                    .font(.body.bold())
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("More", systemImage: "ellipsis") {
                    }
                }
            }
        }
    }
}

Also filed FB19434244 Mitigating overlapping text for sticky section headers for a plain List with .scrollEdgeEffectStyle:

For a List with .listStyle(.plain),

  • for .scrollEdgeEffectStyle(.soft, for: .top), the headers should be un-pinned or there should be a modifier to make them un-pinned

  • for .scrollEdgeEffectStyle(.hard, for: .top), the pinned header view should attach to the top bar and get a blurry material background, similar to a safeAreaBar()

🎉 Yay, in Beta 8, if you use .scrollEdgeEffectStyle(.hard, for: .top), the pinned headers appear attached to the safe area bar:

https://swiftui-garden.com/Misc/iOS-26/Overlapping-text-for-pinned-section-headers

Mitigating overlapping text for sticky section headers for a plain List in iOS 26
 
 
Q