33

With a simple List in SwiftUI, how do I change/remove the standard background color for the section header

struct ContentView : View {
    var body: some View {
        List {
            ForEach(0..<4) { section in
                Section(header: Text("Section")) {
                    ForEach(0..<4) { row in
                        Text("Row")
                    }
                }
            }
        }
    }
}

Screenshot with grey section header background

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
David Arve
  • 804
  • 1
  • 7
  • 10

10 Answers10

65

No need to change appearance of all lists or do anything strange, just:

  1. (Optional) Put .listStyle(GroupedListStyle()) on your List if you do not want sticky headers.
  2. Set the listRowInsets on the section to 0.
  3. Set the Section.backgroundColor to clear to remove the default color, or whatever color you want to color it.

Example:

List {
    Section(header: HStack {
        Text("Header")
            .font(.headline)
            .foregroundColor(.white)
            .padding()

            Spacer()
    }
    .background(Color.blue)
    .listRowInsets(EdgeInsets(
        top: 0,
        leading: 0,
        bottom: 0,
        trailing: 0))
    ) {
        // your list items
    }
}.listStyle(GroupedListStyle()) // Leave off for sticky headers

Example List with Section Header Colored

tarrball
  • 2,123
  • 3
  • 23
  • 34
  • 2
    This did not work. All it did for me was push the title (eg, "Header") to the edges and make a thin blue band behind the text. The section header's gray was still present. – P. Ent Nov 12 '19 at 15:55
  • 2
    At first this did not work until I realized I had left .listStyle(GroupedListStyle()) on the List. Once I removed that, this solution also worked. – P. Ent Nov 12 '19 at 16:06
  • I will update my answer to call more attention to that step – tarrball Nov 12 '19 at 16:18
  • 6
    But I do not want GroupedListStyle() I want sticky headers – Michał Ziobro Nov 28 '19 at 09:17
  • 1
    I got good results with this, but had to not use Step 1, and I notice the example above itself does not set GroupedListStyle. – Snips Jan 30 '20 at 11:42
  • Okay... I finally get where I was confused.. I updated my answer to make mention of sticky headers. – tarrball Jan 30 '20 at 13:47
  • If you want to have the header title accurately aligned with the rows, you can use UITableView's default separator inset as the padding: `.padding(.leading, UITableView().separatorInset.left)`. And perhaps create a constant instead of re-initializing UITableView all the time. – bcause Jun 15 '20 at 21:46
  • This seems to force all caps – Brandt Oct 07 '20 at 21:54
  • 1
    Currently this solution doesn't work in landscape on iOS 14.2. It doesn't respect the safe on both sides. – battlmonstr Dec 07 '20 at 11:53
  • How do I add top and bottom padding to the header in question? – Gianni Jun 19 '22 at 10:03
17

The suggested solutions works until you decide to clear your List header background color.

Better solutions for List header custom color:

1.This solution effects all of the List sections in your app: (or move it to your AppDelegate class)

struct ContentView: View {

init() {
      UITableViewHeaderFooterView.appearance().tintColor = UIColor.clear
    }

var body: some View {
    List {
        ForEach(0 ..< 3) { section in
            Section(header:
                    Text("Section")
            ) {
                ForEach(0 ..< 3) { row in
                    Text("Row")
                }
            }
        }
     }
  }
}

2.With this solution you can have custom List header background color for each list in your app:

struct ContentView: View {
init() {
    UITableViewHeaderFooterView.appearance().tintColor = UIColor.clear
}

var body: some View {
    List {
        ForEach(0 ..< 3) { section in
            Section(header:
                HStack {
                    Text("Section")
                    Spacer()
                }
                .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                .background(Color.blue)

            ) {
                ForEach(0 ..< 3) { row in
                    Text("Row")
                }
            }
        }
     }
  }
}
FRIDDAY
  • 3,781
  • 1
  • 29
  • 43
12

In beta 4, relativeWidth was deprecated. Code updated to reflect that.

Unfortunately, there's no quick parameter to set the background color. However, you can still do it:

enter image description here

import SwiftUI

struct ContentView : View {
    var body: some View {
        List {
            ForEach(0...3) { section in
                Section(header:
                            CustomHeader(
                                name: "Section Name",
                                color: Color.yellow
                            )
                        ) {
                    ForEach(0...3) { row in
                        Text("Row")
                    }
                }
            }
        }
    }
}

struct CustomHeader: View {
    let name: String
    let color: Color

    var body: some View {
        VStack {
            Spacer()
            HStack {
                Text(name)
                Spacer()
            }
            Spacer()
        }
        .padding(0).background(FillAll(color: color))
    }
}

struct FillAll: View {
    let color: Color
    
    var body: some View {
        GeometryReader { proxy in
            self.color.frame(width: proxy.size.width * 1.3).fixedSize()
        }
    }
}
ramzesenok
  • 5,469
  • 4
  • 30
  • 41
kontiki
  • 37,663
  • 13
  • 111
  • 125
  • 3
    Doesn't work really well when rotating the device, though. – David Arve Jul 03 '19 at 10:46
  • iPhone XR. But setting a larger relativeWidth fixes it. – David Arve Jul 03 '19 at 10:48
  • Oops. I see it. Updated the code with a larger relative width. – kontiki Jul 03 '19 at 10:51
  • @AndreCarrera The answer was posted back when relativeWidth was a valid option. Code updated. – kontiki Jul 23 '19 at 19:07
  • Section colors are possible (at least, they are now?) using background and edge insets. https://stackoverflow.com/a/58722253/3708323 – tarrball Nov 06 '19 at 02:32
  • While this does work, I noticed that the color you pick isn't accurate. From my testing, all of the RGB values are dropped by -5. So, if you had the color (255, 255, 255), it becomes (250, 250, 250). Anyone know why? – Tino Caer Apr 18 '20 at 03:32
  • 1
    Currently this solution doesn't fully work in landscape on iOS 14.2. It doesn't respect the safe area on the left side. – battlmonstr Dec 07 '20 at 11:51
7

I tried to use the custom header code above, and unfortunately could not get it to work in beta 6. That led me to the use of a ViewModifier:

public struct sectionHeader: ViewModifier{
var backgroundColor:Color
var foregroundColor:Color
public func body(content: Content) -> some View {
    content
    .padding(20)
    .frame(width: UIScreen.main.bounds.width, height: 28,alignment:
    .leading)
    .background(backgroundColor)
    .foregroundColor(foregroundColor)
}}

Which can be added to the sections in your list as follows:

struct ContentView: View {
@ObservedObject var service = someService()
var body: some View {
    NavigationView {
        List{
            ForEach(someService.sections) {section in
                Section(header: Text(section.title).modifier(sectionHeader(backgroundColor: Color(.systemBlue), foregroundColor: Color(.white)))){

Hope that helps somebody!

Clint Pick
  • 75
  • 1
  • Just make sure you do not add .listStyle() modifier to the list as that will impose its own view on the section headers. – P. Ent Nov 12 '19 at 16:02
  • This works on startup, but gets broken on rotation, as the width is not updated properly. Also it doesn't respect the proper safe area size in landscape. – battlmonstr Dec 07 '20 at 12:03
  • Thanks. this is the only solution that works for me. – Duck Apr 15 '21 at 14:13
7

I was able to get the header to be clear (become white in my case) by using the custom modifiers. I needed to use listStyle() modifiers and all of the above didn't work for me.

Hope it helps someone else!

List {
    Section(header: HStack {
        Text("Header Text")
            .font(.headline)
            .padding()

            Spacer()
    }
    ) {
ForEach(0...3) { row in
                        Text("Row")
                    }
    }
}.listStyle(GroupedListStyle()).listSeparatorStyle()

public struct ListSeparatorStyleModifier: ViewModifier {
    public func body(content: Content) -> some View {
        content.onAppear {
            UITableView.appearance().separatorStyle = .singleLine
            UITableView.appearance().separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
            UITableViewHeaderFooterView.appearance().tintColor = .clear
            UITableView.appearance().backgroundColor = .clear // tableview background
            UITableViewCell.appearance().backgroundColor = .clear

        }
    }
}

extension View {
    public func listSeparatorStyle() -> some View {
        modifier(ListSeparatorStyleModifier())
    }
}

Richard Smairat
  • 111
  • 1
  • 8
6

You have to use a rectangle combined with .listRowInsets on the view for your header section

Section(header: headerSectionView) {
    Text("MySection")
}


private var headerSectionView: some View {
    Rectangle()
        .fill(Color.blue) // will make a blue header background
        .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
        .overlay(
            Text("My Section Title")
                .padding(.horizontal), // You need this to add back the padding
            alignment: .leading
        )
}

Xaxxus
  • 1,083
  • 12
  • 19
  • this is the only solution that worked for me – Reshad Sep 14 '21 at 14:38
  • It works, but if I increase the font size there is basically no top and bottom padding. How do I increase that? – Gianni Jun 18 '22 at 10:24
  • You could try making the rectangle the background of the text instead of making the text an overlay. Its been a while since I answered this. I think iOS 14+ has some APIs to do this without the hacks. – Xaxxus Jun 30 '22 at 03:07
4

I found a better solution

UITableViewHeaderFooterView.appearance().backgroundView = .init()
  • 1
    The problem with this is that it could break at any time if apple changes the behaviour of SwiftUI lists. It probably doesn't work anymore in iOS 16 as Lists are no longer backed by UITableView. They now use UICollectionView. So you probably have to use the equivalent appearance API for collection views. – Xaxxus Jun 30 '22 at 03:09
2

Since iOS 16 you could use the scrollContentBackground(_:) modifier.

Reference here

In your example:

struct ContentView : View {
    var body: some View {
        List {
            ForEach(0...3) { section in
                Section(header: Text("Section")) {
                    ForEach(0...3) { row in
                        Text("Row")
                    }
                }
            }
        }
        .scrollContentBackground(.hidden)
    }
}
Teetz
  • 3,475
  • 3
  • 21
  • 34
1

Another way you can do it by setting the frame of the header:

        VStack {
            List {
                ForEach(0...3) { section in
                    Section(header:
                        Text("Section")
                            .frame(minWidth: 0, maxWidth: .infinity,alignment:.leading)
                            .background(Color.blue.relativeWidth(2))
                    ) {
                        ForEach(0...3) { row in
                            Text("Row")
                        }
                    }
                }
            }
        }
Ketan Odedra
  • 1,215
  • 10
  • 35
  • I had to do this to the above to get it to work. **Section(header: Text("Group").frame(minWidth: 0, maxWidth: .infinity,alignment: .leading).background(Color.gray))** – uplearned.com Nov 20 '19 at 20:49
  • 2
    When I did this it mostly worked nicely, however the coloured section header bar does not reach to the sides of the display, so I'm left with a small grey section at each end which is rather disappointing. Do you know how to resolve this? – James Jiimmy Nov 03 '20 at 12:27
  • relativeWidth does not exist on iOS 14.2 – battlmonstr Dec 07 '20 at 11:59
0

iOS 16.4

Removing/Changing section header BG color

Use .listStyle(.plain) modifier to replace sections' header default BG color with a .clear color. To change sections' header color, use the same modifier and add a desired BG color in ZStack.

struct ContentView : View {
    var body: some View {
        ZStack {
            // Color.yellow.opacity(0.1).ignoresSafeArea()
            List {
                ForEach(0..<4) { section in
                    Section(header: Text("Section").font(.title)) {
                        ForEach(0..<4) { row in
                            Text("Row")
                        }
                    }
                }
            }.listStyle(.plain)            // clear color
        }
    }
}

enter image description here

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220