Search code examples
swiftswiftuiopacitytabview

Make TabView Background Transparent


Views in SwiftUI have a transparent background by default. This usually means that they have a white background because that's the default background color of your app. However, this also means that you can use a ZStack to change the background color of your entire app and that color will show through all your views unless you explicitly set their own background color:

struct Main: View {
    var body: some View {
        ZStack {
            Color.orange.edgesIgnoringSafeArea(.all)

            // Sub-view inlined
            VStack {
                Text("Hello World")
                Button("Press Me", action: { print("Pressed") })
            }
        }
    }
}

enter image description here

The problem I have run into is that this is not true for a TabView:

struct Main: View {
    var body: some View {
        ZStack {
            Color.orange.edgesIgnoringSafeArea(.all)

            // Sub-view inlined
            TabView {
                VStack {
                    Text("Hello World")
                    Button("Press Me", action: { print("Pressed") })
                }.tabItem {
                    Text("First Page")
                }
            }
        } 
    }
}

The TabView blocks the background color:

enter image description here

I can change the background color of the subview, but if I make it transparent, the background is white again instead of showing the underlying color in the ZStack. I've also tried sundry other ways to make the TabView transparent, such as setting its background to Color.clear, but to no avail.

TL;DR

Is it possible to make a TabView transparent instead of white?


Solution

  • The hosting view of every tab has system background color (which is opaque).

    demo1

    Here is possible workaround. Tested with Xcode 12 / iOS 14

    demo

    struct BackgroundHelper: UIViewRepresentable {
        func makeUIView(context: Context) -> UIView {
            let view = UIView()
            DispatchQueue.main.async {
                // find first superview with color and make it transparent
                var parent = view.superview
                repeat {
                    if parent?.backgroundColor != nil {
                        parent?.backgroundColor = UIColor.clear
                        break
                    }
                    parent = parent?.superview
                } while (parent != nil)
            }
            return view
        }
    
        func updateUIView(_ uiView: UIView, context: Context) {}
    }
    
    struct ContentView: View {
        var body: some View {
            ZStack {
                Color.orange.edgesIgnoringSafeArea(.all)
    
                // Sub-view inlined
                TabView {
                    VStack {
                        Text("Hello World")
                        Button("Press Me", action: { print("Pressed") })
                    }
                    .background(BackgroundHelper())  // add to each tab if needed
                    .tabItem {
                        Text("First Page")
                    }
                    Text("Second")
                        .background(BackgroundHelper())  // add to each tab if needed
                        .tabItem {
                            Text("Second Page")
                        }
                }
            }
        }
    }