127

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

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
andromedainiative
  • 4,414
  • 6
  • 22
  • 34
  • [This worked for me. Adding it to my ContentView_Previews. When you use a List just make sure what ever stack you have at the top has a spacer in it at the top. The .edgesIgnoringSafeArea(.all) likes to misbehave.](https://i.stack.imgur.com/oF4Sf.png) – Alias111 Mar 21 '20 at 18:46
  • With the help of another answer I was able to color every single element of the list in a different way, like in your screenshot. https://stackoverflow.com/a/69514684/9439097 – charelf Oct 10 '21 at 12:53
  • For any future viewers, here's the **iOS 16** version https://stackoverflow.com/a/72650158/13278922 – Timmy Jul 19 '22 at 18:20

29 Answers29

156

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

Marius Waldal
  • 9,537
  • 4
  • 30
  • 44
  • 26
    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.. – andromedainiative Jun 10 '19 at 21:43
  • 2
    Yeah, it's a bit of a pain. Fun to play with, though. Should be more smoothed out soon, hopefully – Marius Waldal Jun 11 '19 at 16:28
  • 2
    @MariusWaldal Did you find how to change section view background color? – Aliaksandr Bialiauski Jun 29 '19 at 20:12
  • 4
    @andromedainiative Do List { ForEach(items, id: \.self) { item in Text(item).listRowBackground(Color.green) } instead, then it works. – Steve Ham Oct 21 '19 at 22:15
  • 1
    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 – Othyn Oct 15 '20 at 22:04
  • changing the listRowBackground results in losing the selection brief color change when you tap on a navigationLink. That gray highlight that appears briefly when you press a row in a list to push to another view. I can't figure out how to get that back or replicate it. – alionthego Oct 12 '21 at 00:11
  • This removes the color change when the row is selected. Do you know how to keep it? – Gianni Jul 16 '22 at 08:39
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
}
  • 17
    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. – glothais-kwl Jul 01 '20 at 20:31
  • Nice, also can use: UITableView.appearance().separatorColor = .darkGray – MrAn3 Jul 05 '20 at 02:29
  • 1
    @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. – Learn2Code Oct 19 '20 at 02:11
  • 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 } – C. Hellmann Apr 19 '21 at 16:37
  • 3
    @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. – DanSkeel May 26 '21 at 14:22
47

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)
        }
    }
}
Steve Ham
  • 3,067
  • 1
  • 29
  • 36
  • 1
    It works but I am still stuck at figuring out how to change the background of the List itself. – Vladimir Amiorkov Nov 05 '19 at 08:54
  • 23
    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. – Przemyslaw Jablonski Nov 20 '19 at 12:09
  • 7
    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. – Marchy Apr 03 '20 at 18:58
  • 2
    This is fixed in iOS 15. `listRowBackground` works with items within `List(elements)` as well as `List { ForEach(elements) {` – OliverD Oct 04 '21 at 20:54
  • @VladimirAmiorkov you can change the background of the list if the list style is .plain. – Gavin Morrow Jul 10 '22 at 17:47
  • This removes the color change when the row is selected. Do you know how to keep it? – Gianni Jul 16 '22 at 08:40
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.

Vishal Patel
  • 717
  • 6
  • 11
24

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

Cameron McBroom
  • 485
  • 5
  • 8
24

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)
Jordan H
  • 52,571
  • 37
  • 201
  • 351
16

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
        }
    }
}
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
15

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.

Ari
  • 151
  • 1
  • 3
12

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

Jake Strickler
  • 364
  • 2
  • 11
  • 9
    It works, but it messes with the color of the cells - i.e. just tried with red Text as list items - they turn black. – Matteo Pacini Jun 10 '19 at 07:55
  • 1
    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 – Marius Waldal Jun 10 '19 at 08:51
10

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"))
}
Mustafa Ozhan
  • 321
  • 6
  • 9
  • 4
    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 :) – CMash Oct 11 '19 at 18:16
  • This works, but then, I have a section header in the list and that header is now not `sticky`-- it moves with the list :(. – Chris Prince Oct 05 '20 at 03:34
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
            }
        )
    }
}
Amit
  • 71
  • 1
  • 2
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:

charelf
  • 3,103
  • 4
  • 29
  • 51
5

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"))
    }
}
caleb81389
  • 171
  • 1
  • 7
  • Hi @caleb81389, It's working for me but it will not apply to empty rows. Do you have any idea why? – Omri Shapira Jul 28 '19 at 10:36
  • 1
    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. – caleb81389 Jul 28 '19 at 12:14
  • Let me know if you figure it out! – caleb81389 Jul 29 '19 at 03:43
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)
    }
}
Emma K Alexandra
  • 464
  • 3
  • 15
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

Pedro Trujillo
  • 1,559
  • 18
  • 19
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")
      }
...

}
Islom Alimov
  • 454
  • 6
  • 11
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.

Oleg G.
  • 550
  • 5
  • 25
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)
}
}
CodeChanger
  • 7,953
  • 5
  • 49
  • 80
2

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)
sarunw
  • 8,036
  • 11
  • 48
  • 84
  • Even though the [iPadOS 16 Beta Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ipados-16-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](https://developer.apple.com/documentation/swiftui/list/scrollcontentbackground%28_:%29). Perhaps that will change when iPadOS 16 is released. – Oliver Oct 06 '22 at 23:27
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()
    }
}
Andrey Dyatkov
  • 127
  • 1
  • 6
1

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)
    }
}
As If Prince
  • 257
  • 2
  • 10
1

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

Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143
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))
sacriorv
  • 9
  • 1
0

If you want to avoid setting the appearance for all table views globally, you can combine UITableView.appearance(whenContainedInInstancesOf:) with UIHostingController. Thanks DanSkeel for the comment you left above pointing this out. This is how I used it:

public class ClearTableViewHostingController<Content>: UIHostingController<Content> where Content: View {
    public override func viewDidLoad() {
        UITableView.appearance(whenContainedInInstancesOf: [ClearTableViewHostingController<Content>.self]).backgroundColor = .clear
    }
}

You can use ClearTableViewHostingController like this:

let view = MyListView()
let viewController = ClearTableViewHostingController(coder: coder, rootView: view)

Then in your view you can set the list background color like so:

List {
    Text("Hello World")
}
.background(Color.gray)
MattL
  • 1,132
  • 10
  • 23
  • 1
    This is the best solution that I found with a slight change. Instead of using wrapper class, I set appearance in view's init init() { UITableView.appearance(whenContainedInInstancesOf: [UIHostingController.self]).backgroundColor = .color } – Nikolai Prokofev Jun 08 '22 at 14:45
0

Make extension List like:

extension List{
@available(iOS 14, *)
func backgroundList(_ color: Color = .clear) -> some View{
    UITableView.appearance().backgroundColor = UIColor(color)
    return self
}

}

Badre
  • 710
  • 9
  • 17
0

you can use introspect library from Github to set the background color for the underlying table view like this:

List { ... } .introspectTableView { tableView in
                tableView.backgroundColor = .yellow
            }
JAHelia
  • 6,934
  • 17
  • 74
  • 134
0
import SwiftUI

extension View {
    func blurredSheet<Content: View>(_ style: AnyShapeStyle, isPresented: Binding<Bool>, onDismiss: @escaping ()->(), @ViewBuilder content: @escaping ()->Content) -> some View {
        self
            .sheet(isPresented: isPresented, onDismiss: onDismiss) {
                content()
                    .background(RemoveBackgroundColor())
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background {
                        Rectangle()
                            .fill(style)
                            .ignoresSafeArea(.container, edges: .all)
                    }
            }
    }
}

fileprivate struct RemoveBackgroundColor: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        DispatchQueue.main.async {
            uiView.superview?.superview?.backgroundColor = .clear
        }
    }
}
  • 3
    Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Jul 12 '23 at 06:25
-5

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)
Dinar
  • 1
  • while this does work, it also makes the list transparent. I tried ```opacity(0.5)```, ```opacity(1)```, ```opacity(0)``` and none gave a result where the list rows where present and the list background the desired color – charelf Oct 10 '21 at 12:51
  • This is an irrelevant answer, which also won't work. – Ali Pishvaee Nov 23 '21 at 12:47