169

I'm trying to recreate an UI I built with UIKit in SwiftUI but I'm running into some minor issues.

I want the change the color of the List here, but no property seems to work as I expects. Sample code below:

struct ListView: View {
    @EnvironmentObject var listData: ListData

       var body: some View {
        NavigationView {
            List(listData.items) { item in
                ListItemCell(item: item)
            }
            .content.background(Color.yellow) // not sure what content is defined as here
            .background(Image("paper-3")) // this is the entire screen 
        }
    }
}

struct ListItemCell: View {
    let item: ListItem

    var body: some View {

        NavigationButton(destination: Text(item.name)) {
            Text("\(item.name) ........................................................................................................................................................................................................")
                .background(Color.red) // not the area I'm looking for
        }.background(Color.blue) // also not the area I'm looking for
    }
}

I want to change the WHITE area

3

34 Answers 34

185

Ok, I found the solution for coloring the list rows:

struct TestRow: View {

    var body: some View {
        Text("This is a row!")
        .listRowBackground(Color.green)
    }
}

and then in body:

List {
    TestRow()
    TestRow()
    TestRow()
}

This works as I expect, but I have yet to find out how to then remove the dividing lines between the rows...

Sign up to request clarification or add additional context in comments.

7 Comments

SwiftUI is incredibly buggy right now, this works when I use this code like this and hard code my list items. When I load my data async it does not work anymore..
Yeah, it's a bit of a pain. Fun to play with, though. Should be more smoothed out soon, hopefully
@MariusWaldal Did you find how to change section view background color?
@andromedainiative Do List { ForEach(items, id: \.self) { item in Text(item).listRowBackground(Color.green) } instead, then it works.
I had this really stupid issue where the .listRowBackground() modifier HAD to be after the .contextMenu() modifier, otherwise it wouldn't work. Xcode 12.0.1
|
95

iOS 16 provides a modifier to control the background visibility of List (and other scrollable views): scrollContentBackground(_:)

You can hide the standard system background via .hidden. If you provide a background as well, that will become visible.

List {
    Text("One")
    Text("Two")
}
.background(Image("MyImage"))
.scrollContentBackground(.hidden)

You may also want to customize the background of list rows - the individual cells - and separators. This can be done like so:

List {
    Section("Header") {
        Text("One")
        Text("Two")
            .listRowBackground(Color.red)
    }
    .listRowBackground(Color.clear)
    .listRowSeparator(.hidden)
}
.scrollContentBackground(.hidden)

2 Comments

Thank you very much for ".scrollContentBackground(.hidden)"!! It saved my day.
Winner winner! Thank you sir
54

This will set the background of the whole list to green:

init() {
   UITableView.appearance().separatorStyle = .none
   UITableViewCell.appearance().backgroundColor = .green
   UITableView.appearance().backgroundColor = .green
}

6 Comments

How would I vary this across different lists? I applied this to one list in one tab, and this affected the other list in another tab view.
Nice, also can use: UITableView.appearance().separatorColor = .darkGray
@glothais-kwl did you find a way to "reset" the TableView appearances!? Like you I would like this on certain views and not on all of them and need a way to reset it based on view displayed.
It's angering that such an important change (because otherwise your list will be floating over a white background) is so out of context to access. Can also be used like: List { }.onAppear{ UITableView.appearance().separatorStyle = .none UITableViewCell.appearance().backgroundColor = .green UITableView.appearance().backgroundColor = .green }
@glothais-kwl you can use UIAppearance.appearanceWhenContainedInInstancesOfClasses: and pass there a specific subclass of UIHostingController that you will use to present this exact SwiftUI view.
|
52

enter image description here

struct ContentView: View {

    var strings = ["a", "b"]

    var body: some View {

        List {
            ForEach(strings, id: \.self) { string in
                Text(string)
            }.listRowBackground(Color.green)
        }
    }
}

6 Comments

It works but I am still stuck at figuring out how to change the background of the List itself.
This is the only one that works for me as well. I think it's important to emphasise that you have to have List { ForEach(elements) { }} as opposed to List(elements) { } in order for this modifier to work.
Insane that the WAY that you specify the data to the list makes this modifier not work. Doesn't leave good view on SwiftUI as a whole seeing this kind of bugs within it.
This is fixed in iOS 15. listRowBackground works with items within List(elements) as well as List { ForEach(elements) {
@VladimirAmiorkov you can change the background of the list if the list style is .plain.
|
33

You can do it by changing UITableView's appearance.

UITableView.appearance().backgroundColor = UIColor.clear

just put this line in Appdelegate's didFinishLaunchingWithOptions method. In replace of UIColor.clear set whatever color you want to add in background color of list.

5 Comments

It works but it doesn't remove white background under rows that are actually displayed
It do the job: UITableViewCell.appearance().backgroundColor = .clear
Is there a solution for Mac for this?
@davidev my answer below
The question was "How do I modify the background color of a List in SwiftUI?" why are you talking about UITableView.appearance() and AppleDelegate ? :-/ Please read carefully the questions..
25

Changing Background Color

As other have mentioned, changing the UITableView background will affect all other lists in your app.

However if you want different background colors you can set the default to clear, and set the background color in swiftui views like so:

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
// Ignore safe area to take up whole screen
.background(Color.purple.ignoresSafeArea())
.onAppear {
    // Set the default to clear
    UITableView.appearance().backgroundColor = .clear
}

You probably want to set the tableview appearance earlier, such as in the SceneDelegate or root view like so:

// SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
      
    
    guard let windowScene = scene as? UIWindowScene else {
        print("Returning because screne does not exist")
        return
            
    }
    
    // Set here    
    UITableView.appearance().backgroundColor = .clear
    let contentView = ContentView()
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = UIHostingController(rootView: contentView)
    self.window = window
    window.makeKeyAndVisible()
}


// Root App View
@main
struct ListBackgroundApp: App {
    
    init() {
        UITableView.appearance().backgroundColor = .clear
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Working Background Color Change

1 Comment

The trick of setting the backgroundColor to clear, lets you even usage Images as backgrounds! Thank you so much
19

2022

MacOS Solution

The following code makes ALL OF Lists background color transparent:

// Removes background from List in SwiftUI
extension NSTableView {
    open override func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        
        backgroundColor = NSColor.clear
        if let esv = enclosingScrollView {
            esv.drawsBackground = false
        }
    }
}

enter image description here

..........

..........

..........

the following code makes ALL OF TextEditors background color transparent:

extension NSTextView {
    open override var frame: CGRect {
        didSet {
            backgroundColor = .clear
            drawsBackground = true
        }
    }
}

3 Comments

Awesome! SwiftUI should make this default lol
holy God, you can override in extension?
@PeterLapisu as you see you can :) hehehe
17

There is an argument: listRowBackground() in SwiftUI, but if you use List directly to iterate the data collection, it doesn't work.

Here is my workaround:

    List {
        // To make the background transparent, we have we use a ForEach as a wrapper
        ForEach(files) {file in
            Label(
                title: { Text(file.name ?? fileOptionalFiller).lineLimit(listRowTextLineLimit) },
                icon: { AppIcon.doc.foregroundColor(.primary) }
            )
        }
        .listRowBackground(Color.primary.colorInvert())
    }

Basically, listRowBackground() works if you use a ForEach inside List.

2 Comments

The trouble with ForEach is that you can't use collapsible nested lists AFAICT. On my phone (iOS 17.0.2) the background colour of a nested list item is hardwired to black. Not even .listRowBackground changes it. I can't get the background of the item's Text body to expand to fill the entire row, which seems to have some (also hardwired) padding.
This solution doesn't work with list style grouped.
13

I was able to get the whole list to change color by using colorMultiply(Color:). Just add this modifier to the end of the list view, and then the padding will push the table to the device edges. For example:

List {...}.colorMultiply(Color.green).padding(.top)

https://www.hackingwithswift.com/quick-start/swiftui/how-to-adjust-views-by-tinting-and-desaturating-and-more

2 Comments

It works, but it messes with the color of the cells - i.e. just tried with red Text as list items - they turn black.
Yes, unfortunately, blending is not what you want. You want it to be layered, with the background view color as, well, background, and then the cell color and/or text color to be on top of that
13
List {
         ForEach(dataArray, id:\.self) { item in
             Text("\(item)")
                 .font(.title3)
         }
         .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0)) // EdgeInsets
         .listRowBackground(Color.clear) // Change Row Color
         .listRowSeparator(.hidden) //hide Seprator
     }
     .listStyle(.plain) //Change ListStyle
     .scrollContentBackground(.hidden)
     .background(Color.yellow) //Change Background Color

enter image description here

Comments

11

I do not know what is the connection but if you wrap the list with Form it is working.

Form {
     List(viewModel.currencyList, id: \.self) { currency in
        ItemView(item: currency)
     }
      .listRowBackground(Color("Primary"))
      .background(Color("Primary"))
}

2 Comments

As Caleb points out the listRowBackground shouldn't be applied to the List but to the row item itself. Also you should be able to just do Color("Primary") instead of force unwrapping a UIColor :)
This works, but then, I have a section header in the list and that header is now not sticky-- it moves with the list :(.
7

The following solution changes all sorts of colors in SwiftUI's List without changing the global appearance() of the UITableView. You can change the cell background as well as the List background.

import SwiftUI

struct ContentView: View {
    var listItems: [String] = ["A", "B", "C"]
    
    var body: some View {
        List {
            ForEach(listItems, id: \.self) { item in
                Text(item)
            }
            .listRowBackground(Color.purple) // Applying the list cell background color
        }
        .scrollContentBackground(.hidden) // Hides the standard system background of the List
        .background(Color.teal) // Applying list background color
    }
}

Output: Demo

1 Comment

Is it possible to apply gradient in the background of entire list section?
7
struct Details: View {

    var body: some View {
        Spacer().overlay(
            List {
                Text("Hello World!").font(.title2)
                    .listRowBackground(Color.clear)
                Text("Hello World again").font(.title2)
                    .listRowBackground(Color.clear)
            }.onAppear() {
                UITableView.appearance().backgroundColor = UIColor.green
                UITableViewCell.appearance().backgroundColor = UIColor.green
            }
        )
    }
}

Comments

7

The answer by Islom Alimov https://stackoverflow.com/a/59970379/9439097 seems to be the best implementation so far in my opinion.

Only drawback: this also changes the background color of all other list views in your app, so you need to manually change them back unless you want the same color everywhere.

Here is an example view:

import SwiftUI

struct TestView1: View {
    
    init(){
        UITableView.appearance().backgroundColor = UIColor(Color.clear)
    }
    
    @State var data = ["abc", "def"]
    
    var body: some View {
        VStack {
            List {
                ForEach(data, id: \.self) {element in
                    Text("\(String(describing: element))")
                }
                .background(Color.green)
                .listRowBackground(Color.blue)
                
            }
            .background(Color.yellow)
            Spacer()
            Color.red
        }
    }
}

struct TestView1_Previews: PreviewProvider {
    static var previews: some View {
        TestView1()
    }
}

produces:

Comments

6

Someone may find this useful if attempting to create a floating type cell with SwiftUI using .listRowBackground and applying .padding

var body: some View {
    NavigationView {
        List {
            ForEach (site) { item in
                HStack {
                    Text(String(item.id))

                    VStack(alignment: .leading) {
                        Text(item.name)
                        Text(item.crop[0])
                    }

                }.listRowBackground(Color.yellow)
                      .padding(.trailing, 5)
                      .padding(.leading, 5)
                      .padding(.top, 2)
                      .padding(.bottom, 2))
            }
        }
            .navigationBarTitle(Text("Locations"))
    }
}

3 Comments

Hi @caleb81389, It's working for me but it will not apply to empty rows. Do you have any idea why?
No sorry. I have no idea how to apply that to empty rows. Maybe you could artificially put white space text “ “ in a label or something...? Other than that SwiftUI kinda does what it wants to and something like that is gonna take someone spatter than me to answer.
Let me know if you figure it out!
6

2024 answer, they seem to change the List implementation often. In fact, it used to use a TableView now it uses a CollectionView. edgesIgnoringSafeArea is what made it work for me.

List {
  ...
}
.modifier(ListBackgroundModifier())
struct ListBackgroundModifier: ViewModifier {

    @ViewBuilder func body(content: Content) -> some View {
        if #available(iOS 16.0, *) {
            content
                .scrollContentBackground(.hidden)
                .background(Color(UIColor.background).edgesIgnoringSafeArea(.all))
        } else {
            content
        }
    }
}

OR if using .listStyle(InsetListStyle()) you can just do this on each item in the list: .listRowBackground(Color(UIColor.background).edgesIgnoringSafeArea(.all))

1 Comment

I would extend with convenient func: extension View { func listBackgroundColor(_ color: Color) -> some View { modifier(ListBackgroundModifier(color: color)) } } and add property let color: Color to modifier
4

In iOS 16, we got a native way to do this via scrollcontentbackground modifier.

You can either change the color by setting a color (ShapeStyle) to scrollcontentbackground.

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
.scrollContentBackground(Color.pink)

Or you can hide the background .scrollContentBackground(.hidden) and set a custom one with .backgroud modifier.

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
.background {
    Image("ventura")

}
.scrollContentBackground(.hidden)

1 Comment

Even though the iPadOS 16 Beta Release Notes say that .scrollContentBackground "permits customisation of background colours for scrollable views like List" it is still currently limited to Visibility according to the documentation. Perhaps that will change when iPadOS 16 is released.
3

I assume the listRowPlatterColor modifier should do this, but isn't as of Xcode 11 Beta 11M336w

var body: some View {
    List(pokemon) { pokemon in
        PokemonCell(pokemon: pokemon)
            .listRowPlatterColor(.green)
    }
}

1 Comment

listRowPlatterColor is now deprecated and workend only on watchOS.
3

.colorMultiply(...)

As an option you can .colorMultiply(Color.yourColor) modifier.

Warning: this does not change the color! This only applies the Multiply modifier to the current color. Please read the question before any action, because you are probably looking for: "How to CHANGE the background color of a List in SwiftUI" and this will not work for you. ❄️

Example:

List (elements, id:\.self ) { element in

     Text(element)

}
.colorMultiply(Color.red) <--------- replace with your color

enter image description here

6 Comments

works well, however if user switches to dark mode it fails miserably. I expect this is SwiftUI bug
this just overlays a cooler and multiply, it doesn't change current color
@ManeManero I wonder if you read the question of the post: "How do I modify the background color of a List in SwiftUI?" This does not change the color, it modifies it. And for dark mode, yup, you need to add more logic/conditions for specific changes.
This will mess up with the row background color
does not work properly with dark theme, this solution is useless.
|
3

For some reason color change is not working, you can try the .listStyle to .plain

Code:

struct ContentView: View {
var body: some View {
    VStack {
        Text("Test")

        List {
            ForEach(1 ..< 4) { items in
                Text(String(items))
            }
        }
        .listStyle(.plain)
    }
}

Comments

2

For me, a perfect solution to change the background of List in SwiftUI is:

struct SomeView: View {
    init(){
    UITableView.appearance().backgroundColor = UIColor(named: "backgroundLight")
      }
...

}

Comments

2

List is not perfect yet.

An option would be to use it like this -> List { ForEach(elements) { }} instead of List($elements)

On my end this is what worked best up to now. Like @FontFamily said, it shouldn't break any List default behaviors like swiping.

3 Comments

This kills the functionality of onDelete and onMove for nested list items.
Does it kill it for the second example I gave? I think with the List { ForEach(elements) } it should be fine right?
You are right - it does work for the second example you gave.
2

Simply Add UITableView appearance background color in init() method and add list style (.listStyle(SidebarListStyle()). Don't forget to import UIKit module

struct HomeScreen: View {
init() {
    UITableView.appearance().backgroundColor = .clear
}

let tempData:[TempData] = [TempData( name: "abc"),
                         TempData( name: "abc"),
                         TempData( name: "abc"),
                         TempData( name: "abc")]

var body: some View {
    ZStack {
        Image("loginBackgound")
            .resizable()
            .scaledToFill()
        List{
            ForEach(tempData){ data in
                Text(data.name)
            }
        }
        .listStyle(SidebarListStyle())
        
    }
    .ignoresSafeArea(edges: .all)
}
}

Comments

2

Changing background did not work for me, because of the system background. I needed to hide it.

List(examples) { example in
        ExampleRow(example: example)
    }.background(Color.white.edgesIgnoringSafeArea(.all))
        .scrollContentBackground(.hidden)

Comments

1

Using UITableView.appearance().backgroundColor is not a good idea as it changes the backgroundColor of all tables. I found a working solution for color changing at the exact table you selected in iOS 14, 15.

We will change the color using a modifier that needs to be applied inside the List

extension View {
    
    func backgroundTableModifier(_ color: UIColor? = nil) -> some View {
        self.modifier(BackgroundTableModifier(color: color))
    }

}

Our task is to find the UITableView and after that change the color.

private struct BackgroundTableModifier: ViewModifier {
    
    private let color: UIColor?
    @State private var tableView: UITableView?
    
    init(color: UIColor?) {
        self.color = color
    }
    
    public func body(content: Content) -> some View {
        if tableView?.backgroundColor != color {
            content
                .overlay(BackgroundTableViewRepresentable(tableBlock: { tableView in
                    tableView.backgroundColor = color
                    self.tableView = tableView
                }))
        } else {
            content
        }
    }
}

private struct BackgroundTableViewRepresentable: UIViewRepresentable {
    
    var tableBlock: (UITableView) -> ()
    
    func makeUIView(context: Context) -> BackgroundTableView  {
        let view = BackgroundTableView(tableBlock: tableBlock)
        return view
    }
    
    func updateUIView(_ uiView: BackgroundTableView, context: Context) {}
}

class BackgroundTableView: UIView {
    
    var tableBlock: (UITableView) -> ()
    
    init(tableBlock: @escaping (UITableView) -> ()) {
        self.tableBlock = tableBlock
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        if let tableView = findTableView(in: self) {
            tableBlock(tableView)
        }
    }
    
    private func findTableView(in view: UIView) -> UITableView? {
        if let tableView = view as? UITableView {
            return tableView
        }
        
        if let superView = view.superview {
            return findTableView(in: superView)
        }
        
        return nil
    }
    
}

In order to find UITableView, the modifier must be inside the List. Naturally, you need to ensure that the modifier is called only once, you do not need to apply it to each row. Here is an example of usage

List {
   rows()
     .backgroundTableModifier(.clear)
}

func rows() -> some View {
    ForEach(0..<10, id: \.self) { index in
        Row()
    }
}

1 Comment

Thank you ! this works perfectly and solves the issue of colouring all the tableViews in the app !
1

Before and After modifiers applied

iOS 16

struct SomeView: View {
    var body: some View {
        ZStack {
            List {
                ForEach(0...5, id: \.self) { i in
                    Text("Sample text")
                } //: LOOP
                .listRowBackground(Color.clear)
                .listRowSeparator(.hidden)
            } //: LIST
            .scrollContentBackground(.hidden)
        } //: ZSTACK
        .background(
            Color.yellow.ignoresSafeArea(.all)
        )
    }
}

Comments

1

For iOS 15 or earlier, doing this once

    UITableView.appearance().backgroundColor = .clear

makes the .background view modifier work on any list after that. So in my app I just do

@main
struct MyApp: App
{
    init()
    {
        // Work around SwiftUI's ridiculous lack of a way to set a list view's background color.
        // Oddly, this will enable the .background view modifier to work on lists from now on.
        UITableView.appearance().backgroundColor = .clear
    }
...
}

After that, in any SwiftUI view you can do:

List(msgs) { aMsg in
        Text(aMsg.senderID)
}
.background(Color.red)

Comments

1

The solution that works for me

List($someData, id: \.id) { item in
     CellView(item: item)
         .listRowBackground(Some Row Background Color) // color of the rows
}
.scrollContentBackground(.hidden) // should be set to .hidden to be able to set custom color
.background(Some List Background Color) // set the color of the list itself

Comments

0

I've inspired some of the configurator used to config per page NavigationView nav bar style and write some simple UITableView per page configurator not use UITableView.appearance() global approach

   import SwiftUI

    struct TableViewConfigurator: UIViewControllerRepresentable {

        var configure: (UITableView) -> Void = { _ in }

        func makeUIViewController(context: UIViewControllerRepresentableContext<TableViewConfigurator>) -> UIViewController {

            UIViewController()
        }

        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TableViewConfigurator>) {

            let tableViews = uiViewController.navigationController?.topViewController?.view.subviews(ofType: UITableView.self) ?? [UITableView]()

            for tableView in tableViews {
                self.configure(tableView)
            }
        }
    }

Then there is UIView extension needed to find all UITableViews

extension UIView {
    func subviews<T:UIView>(ofType WhatType:T.Type) -> [T] {
        var result = self.subviews.compactMap {$0 as? T}
        for sub in self.subviews {
            result.append(contentsOf: sub.subviews(ofType:WhatType))
        }
        return result
    }
}

And usage at the end is:

List {

}.background(TableViewConfigurator {
    $0.backgroundColor = .red
})

Maybe one thing should be improved that is usage of navigationController?.topViewController to make it work even without navigationController in view controllers hierarchy

Comments

0

If anyone came here looking for solutions for background in landscape not full width on iPhone X/11 try:

.listRowBackground(Color("backgroundColour").edgesIgnoringSafeArea(.all))

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.