10

In my app, I want to construct a single background view under my NavigationStack and then show that same background underneath other views that I navigate to via NavigationLinks. I am trying to do this by putting the NavigationStack and my background in a ZStack, but after inspecting the view hierarchy of the app I can see that the NavigationStack is showing its own background in the hosting view controller, and I can't for the life of me figure out how to make it transparent.

Here is a simplified version of my view:

struct Drawer: View {
    @State private var selection: DrawerMenuItem? = DrawerMenuItems().options.first

    var body: some View {
        ZStack {
            Color.blue.ignoresSafeArea()
            
            NavigationStack(path: $path) {
                List(DrawerMenuItems().options, selection: $selection) { item in
                    NavigationLink(destination: AnyView(item.view)) {
                        //Trimmed for brevity
                    }
                }
                .scrollContentBackground(.hidden)
            }
        }
    }
}

And here is a screenshot of the resulting view hierarchy:

View Hierarchy

I am currently using Xcode 15 beta with Swift 5, targeting iOS 17.

1
  • 1
    It's a crying shame that you can't just set .background(Color.clear) on the NavigationStack. Utter nonsense. Commented Sep 19, 2024 at 20:23

2 Answers 2

17

There no direct way to remove NavigationStack background color.

There is an excellent library called SwiftUIIntrospect that provides additional functionality for SwiftUI views. Using that we can access underlying UIKit components of SwiftUI view and we can manipulate that.

with help of this library we can remove the background of NavigationStack

import SwiftUI
import SwiftUIIntrospect

struct Drawer: View {
    var body: some View {
        ZStack {
            Color.blue.ignoresSafeArea()
            NavigationStack {
                Text("Hello")
            }
            .introspect(.navigationStack, on: .iOS(.v16, .v17)) {
                $0.viewControllers.forEach { controller in
                    controller.view.backgroundColor = .clear
                }
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Amazing, this library seems to work exactly as promised. Having some trouble adapting this to NavigationSplitView but I'll ask in their repo.
better to use @_spi(Advanced) import SwiftUIIntrospect and .ios(.v16...)
This is the only solution I could find for this, thanks! I had the same issue except with NavigationView.
Unfortunately, when I tried this it didn't remove the background from any detail views navigated to via NavigationLink.
5

There is now a better way to accomplish this that doesn't require a 3rd party library -- using containerBackground(_, for: ). I'll include some modified code based on your original code. I put explanations inside the code, but definitely feel free to ask questions about it or comment to better explain/clarify any of it.

struct Drawer: View {
    @State private var selection: DrawerMenuItem? = DrawerMenuItems().options.first

    var body: some View {
        ZStack {
            Color.blue.ignoresSafeArea()
            
            NavigationStack(path: $path) {
                List(DrawerMenuItems().options, selection: $selection) { item in
                    NavigationLink(destination: AnyView(item.view)) {
                        // Just put the destination code here
                        // Ex: `blueDestination` or `customBackgroundDestination`
                    }
                }
                .scrollContentBackground(.hidden)
            }
        }
    }

    /// This shows how to just apply a ShapeStyle (e.g., a Color like blue or clear) to the background.
    @ViewBuilder
    private var blueDestination: some View {
        DestinationView() // Placeholder
            .containerBackground(.blue, for: .navigation)
    }

    /// This shows how to apply a custom View to the background of the container.
    /// You could also just use the version above and use `.clear` and then apply a custom `.background` on the destination view.
    @ViewBuilder
    private var customBackgroundDestination: some View {
        DestinationView() // Placeholder
            .containerBackground(for: .navigation) {
                CustomBackground()
            }
    }
}

1 Comment

This is a good solution. However, .containerBackground is only available on iOS 17, and .navigation is only available on iOS 18. So there are some constraints, unfortunately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.