25

With the new SwiftUI update in iOS 16 List no longer depends on UITableView. So the following snippet we used to set the List background color to .clear is now useless:

UITableView.appearance().backgroundColor = .clear

I saw that someone used introspect to solve the problem, but does anyone know of another maybe cleaner way to achieve the same behavior?

Also note that on macOS, the following works fine (Tested using Xcode 14 beta & macOS Ventura):

extension NSTableView {
    open override func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        backgroundColor = NSColor.clear
        enclosingScrollView!.drawsBackground = false
    }
}
Timmy
  • 4,098
  • 2
  • 14
  • 34

4 Answers4

34

iOS 16

Update: Xcode 14b3+

Just use new modifier:

    List {
        Text("Item 1")
        Text("Item 2")
        Text("Item 3")
    }
    .scrollContentBackground(Color.red)     // << here !!
//    .scrollContentBackground(Color.clear)     // << transparent !!
//    .scrollContentBackground(.hidden)     // << can be combined with above !!

Original

Now they use UICollectionView for backend, so an updated workaround is to change corresponding background colors:

demo

Main part:

extension UICollectionReusableView {
    override open var backgroundColor: UIColor? {
        get { .clear }
        set { }

        // default separators use same color as background
        // so to have it same but new (say red) it can be
        // used as below, otherwise we just need custom separators
        // 
        // set { super.backgroundColor = .red }

    }
}

struct ContentView: View {
    init() {
        UICollectionView.appearance().backgroundColor = .clear
    }
//...

Test module on GitHub

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 2
    This was a great help, although when I override the `backgroundColor` getter, I lose my `listRowSeparatorTint` (As can be seen in your image). Any idea how to get it back? – Darren Jun 25 '22 at 17:22
  • @Darren, see updated in comments – Asperi Jun 26 '22 at 11:38
  • Thank you. That's a shame it uses the same as background. When you say about custom separators, do you mean adding an actual line to each row's view? – Darren Jun 26 '22 at 11:57
  • 1
    Yes, they do the same, actually even worse, because adding SwiftUI line just adds drawing line to context, but Apple adds new UIView for each separator line. – Asperi Jun 26 '22 at 12:00
  • 2
    The new `.scrollContentBackground` in beta 3 makes everything work as expected without this workaround and different color separators. – Darren Jul 07 '22 at 08:55
  • 4
    I think the new beta doesn't allow `scrollContentBackground` to be used directly with `Style`. It should be set to `.hidden` and then composed with `.background(style)`. – Jay Lee Aug 12 '22 at 16:04
  • Asperi coming with the fire again! At the time of my post, no where does anyone else say to use UICollectionView. All suggestions say to use UITableView. I get that they changed it, but for folks like me who need to use a List, because of the .refreshable modifier, and still need to support 15, this is the top answer currently. Thank you Asperi! – user1302387 Sep 29 '22 at 20:30
  • Works for iOS16, but how can i fix iOS14? `extension UICollectionReusableView` would also override other usages which is a problem in older, large projects – Deitsch Oct 12 '22 at 15:03
  • 1
    Cannot convert value of type 'Color' to expected argument type 'Visibility' – user2619824 Mar 11 '23 at 05:40
  • `scrollContentBackground` API only supports visibility, not color in latest XCode (14.3.1), and even that is not respected by the `List` if it's style is `.plain`. It ignores both the `.scrollContentBackground` and the `.background` – Mikel Aug 01 '23 at 04:46
26

For that purpose, I created a custom identifier that hides this custom scroll background.

struct ListBackgroundModifier: ViewModifier {

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

Usage:

List {
    ...
}
.modifier(ListBackgroundModifier())
Marian König
  • 704
  • 6
  • 11
  • Yes, if you want it to have the same style all over the app. It wasn't the case in mine. – Marian König Jul 20 '22 at 11:29
  • I noticed another bug, when the list is empty, it will ignore this modifier. In that case just place a thin 1 pixel Color line like this (replace color with your background): Color.red.frame(height: 1).frame(maxWidth: .infinity) .listRowInsets(EdgeInsets()) .listRowSeparatorTint(.clear) .listRowSeparator(.hidden) .listRowBackground(Color.red) And call on List: .environment(\.defaultMinListRowHeight, 1). Beware: preview will work without this fix, just not on the device/simulator. – frin Sep 23 '22 at 11:14
15

iOS 16 adds a new modifier, scrollContentBackground(Visibility), to customize the background visibility for scrollable views including List.

You can hide the standard system background like so which will reveal the List's background if you provide one:

List {
    Text("One")
    Text("Two")
    Text("Three")
}
.background(Image("MyImage"))
.scrollContentBackground(.hidden)
Jordan H
  • 52,571
  • 37
  • 201
  • 351
7

As of Xcode 15.0 beta 6, the working code is

List {
    Text("1")
    Text("2")
    Text("3")
}
.scrollContentBackground(.hidden)
.background(.purple)
Lawrence Gimenez
  • 2,662
  • 4
  • 34
  • 52
  • 1
    This is the one that worked for me. I'm struggling to understand why they make it so hard to change the color of a list? – Craig_VG Jun 28 '23 at 20:33