79
votes

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

17

17 Answers

88
votes

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...

37
votes

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)
        }
    }
}
37
votes

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

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

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.

11
votes

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

11
votes

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.

8
votes

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"))
}
5
votes

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

4
votes

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
votes

.colorMultiply(...)

For the List background use .colorMultiply(Color.yourColor) modifier

Example:

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

     Text(element)

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

enter image description here

3
votes
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
            }
        )
    }
}
2
votes

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)
    }
}
2
votes

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.

1
votes

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

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

}
0
votes

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

0
votes

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

.listRowBackground(Color("backgroundColour").edgesIgnoringSafeArea(.all))
-1
votes

Xcode Version 12.4

The Background property worked for me, but with the mandatory use of Opacity. Without opacity it is not work.

List {
            ForEach(data, id: \.id) { (item) in
                ListRow(item)
                    .environmentObject(self.data)
            }
        }
        .background(Color.black)
        .opacity(0.5)