MTRNord
MTRNord

Reputation: 155

Prevent Sheet Background to be white when navigating with NavigationLink within sheet

I am trying to have a sheet with 2 buttons to navigate to different views. This works fine with a NavigationView + NavigationLink. It navigates as expected to another view.

However I am dealing with this:

First I as expected see the presentationBackground: The sheet has a ultraThinBackground Style and behaves as expected

However then navigating with a NavigationLink leads to a white background:

The sheet now has a fully white background instead of the prior ultraThinBackground style

I am new to swiftui so I may be missing the obvious here. I tried setting both presentationBackground and background on both the destination paramter of the NavigationLink and the content within the new view. Those all seemed to have no affect.

Example Code:

import Foundation
import SwiftUI

struct PlaceHolderView_Demo: View {
    @Binding fileprivate var currentSheetOpeningLevel: PresentationDetent

    var body: some View {
        VStack(alignment: .leading) {
            Text("Meow")
        }.onAppear {
            currentSheetOpeningLevel = PresentationDetent.large
        }.background(Color.clear, ignoresSafeAreaEdges: .all)
    }
}

struct ContentView: View {
    @State private var currentSheetOpeningLevel = PresentationDetent.medium

    var body: some View {
        NavigationView {
            MapView()
                .edgesIgnoringSafeArea(.all)
                .sheet(isPresented: .constant(true)) {
                    NavigationView {
                        InfoSheetView_Demo(currentSheetOpeningLevel: $currentSheetOpeningLevel)
                    }
                    .navigationViewStyle(.stack)
                    .presentationDetents([.medium, .large], selection: $currentSheetOpeningLevel)
                    .interactiveDismissDisabled(true)
                    .presentationBackgroundInteraction(.enabled)
                    .presentationBackground(.ultraThinMaterial)
                }
        }
        .navigationViewStyle(.stack)
    }
}

struct InfoSheetView_Demo: View {
    @Binding var currentSheetOpeningLevel: PresentationDetent
    @State private var searchText = ""

    var body: some View {
        VStack(alignment: .leading) {
            InfoSheetContent(currentSheetOpeningLevel: $currentSheetOpeningLevel).padding()
            Spacer()
        }
        .searchable(text: $searchText, prompt: Text("Search for your Destination"))
    }

    struct InfoSheetContent: View {
        @Binding fileprivate var currentSheetOpeningLevel: PresentationDetent
        @Environment(\.isSearching) private var isSearching
        @State private var searching: Bool = false

        var body: some View {
            if !searching {
                VStack(alignment: .leading) {
                    HStack(alignment: .top, spacing: 10) {
                        Button(action: {}) {
                            Label("Karte", systemImage: "map")
                                .labelStyle(DefaultLabelStyle())
                                .frame(maxWidth: .infinity)
                        }
                        .buttonStyle(.borderedProminent)
                        .controlSize(.large)

                        NavigationLink(destination: PlaceHolderView_Demo(currentSheetOpeningLevel: $currentSheetOpeningLevel)) {
                            Label("Mitnehmen", systemImage: "figure.wave")
                                .labelStyle(DefaultLabelStyle())
                                .frame(maxWidth: .infinity)
                        }
                        .buttonStyle(.bordered)
                        .controlSize(.large)
                    }
                    .padding()
                }
                .background(.thinMaterial)
                .frame(maxWidth: .infinity, maxHeight: 120)
                .cornerRadius(15)
            }
        }
    }
}

struct ContentView_Demo_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Upvotes: 1

Views: 317

Answers (1)

Pat W
Pat W

Reputation: 150

The answer here describes what the problem is: SwiftUI is injecting a View that has a systemBackground color and which we cannot control from SwiftUI.

You can use Introspect to work around the problem. Introspect allows you to find that view and manually change the color to .clear and also remove the shadow. Note that the push and pop animations do not work well with a clear background - so your result will look like this.

Animated push transition

I worked around this problem by using NavigationTransitions to change the transition to a cross fade. The result looks fine:

Animated crossfade transition

Here is the source for the cross fade View. You can comment in the code and remove the .navigationTransition if you prefer the push/pop slide effect.

import MapKit
import NavigationTransitions
import SwiftUI
import SwiftUIIntrospect

struct ContentView: View {

    @State var navigationPath: [SubSheet] = []
    var body: some View {
        Map(initialPosition: .region(.applePark))
            .ignoresSafeArea(.all)

            .sheet(isPresented: .constant(true)) {
                NavigationStack(path: $navigationPath) {
                    VStack {
                        Text("Test")
                        NavigationLink(
                            "Activate SubSheet", value: SubSheet.subSheetOne)
                    }
                    .navigationDestination(for: SubSheet.self) { something in

                        Text("SubSheet")
                            .introspect(
                                .navigationStack, on: .iOS(.v18),
                                scope: .ancestor
                            ) { something in

                                let allsubviews = something.view.allSubViews
                                allsubviews.forEach { view in
                                    if view.backgroundColor == .systemBackground
                                        && view.debugDescription.contains(
                                            "NavigationStackHostingController")
                                    {

                                        // We found the child view that has the systemBackground we don't want, so we remove it.
                                        view.backgroundColor = nil
                                    }

                                    // if you want to use the push/pop transition, you can comment in
                                    // this code to remove the shadow too.
                                    /*
                                     if view.backgroundColor != .black && view.backgroundColor?.cgColor.alpha == 0.1 {
                                        print("changing from \(view.backgroundColor)")
                                        view.backgroundColor = nil
                                     }
                                     */
                                }
                            }
                            .navigationTitle("SubSheet")

                    }

                }
                .navigationTransition(.fade(.cross))  // remove this if you really want the push/pop slide effect

                .scrollContentBackground(.hidden)
                .backgroundStyle(.clear)
                .presentationDetents([.fraction(0.33)])
                .presentationBackgroundInteraction(
                    .enabled(upThrough: .fraction(0.33))
                )
                .presentationBackground(.ultraThickMaterial)

            }

    }
}

enum SubSheet: Hashable {
    case subSheetOne
}

extension UIView {

    /// Retrieving All Subviews from a UIView
    fileprivate var allSubViews: [UIView] {
        return subviews.flatMap { [$0] + $0.allSubViews }
    }
}

extension MKCoordinateRegion {
    /// Apple Mark Region
    static var applePark: MKCoordinateRegion {
        let center = CLLocationCoordinate2D(
            latitude: 37.334606, longitude: -122.009102)
        return .init(
            center: center, latitudinalMeters: 10000, longitudinalMeters: 10000)
    }
}

#Preview {
    ContentView()
}

Upvotes: 0

Related Questions