1

I have a swiftUI view that is presented using a hostingController:

let hostingController = UIHostingController(rootView: FirstView)
 someVC.present(hostingController, animated: true)

The FirstView in turn presents a second SwiftUI view using .sheet(IsPresented with a boolean to determine whether it should be displayed as follows:

struct FirstView: View {
    @Environment(\.presentationMode) var presentationMode

    @State var showSecondView = false
//change showSecondView to true so it displays as a sheet

 .sheet(isPresented: $showSecondView) {
                SecondView() 
            }
}

In SecondView, I have a button that I would like to use to dismiss both the SecondView and the FirstView as well. It dismisses the SecondView but I can't figure out how to dismiss the FirstView as well this since the FirstView was presented in a hosting controller not using a boolean.

If the SecondView was say an Alert within the First View, then I could use the environmental varialbe presentationMode to dismiss, however, that variable does not seem to be accessible from SecondView.

 struct SecondView: View {
        @Environment(\.dismiss) var dismiss
    
    
    Button("OK") {
//THIS DOES NOT WORK BECAUSE presentationMode is out of scope
                presentationMode.wrappedValue.dismiss()
  
                dismiss()//
            }
    }

How can I dismiss not only the current (or Second) sheet but also the presenting (or First view) which was presented in a hostingController?

0

2 Answers 2

1

Sometimes you would pass an action closure, e.g.

 .sheet(isPresented: $showSecondView) {
    SecondView {
        showSecondView = false
    }
}
struct SecondView: View {
    let action: () -> ()
    
    ..    

    Button("OK") {
        action()
    }

And sometimes the closure contains some data you want to pass back, e.g.

struct SecondView: View {
    @State var text = ""
    let action: (String) -> ()
    
    ..    

    Button("OK") {
        action(text)
    }

You can use the same technique to call back into UIKit when you init the root view for the hosting controller.

Sign up to request clarification or add additional context in comments.

4 Comments

This makes sense. Are you suggesting I pass the secondView the presentationMode from the firstView which seems able to dismiss the original VC? I will try that
Is there a straightforward way to pass the varible presentationMode to the secondView?
no need to use presentationMode at all
There may be a better way but I got it to work with the presentationMode variable. I passed it as SecondView(presentationMode: self.presentationMode). It is accepted in SecondView using @Binding var presentationMode: PresentationMode. Finally I called it using $presentationMode.wrappedValue.dismiss()
1

SecondView is created as a SwiftUI view sheet - it is managed internally by SwiftUI and can be dismissed using @Environment(\.dismiss).

But the FirstView is hosted via UIKit (UIHostingController), so it must be dismissed via UIKit (someVC.dismiss(...)), which cannot be done automatically from within a deeply nested SwiftUI view without a bridge.

Here's a suggestion:

  1. Let's create an ObservableObject so that we can use it with a similar approach of managing environment objects:

    class DismissController: ObservableObject {
    
        let dismissSubject = PassthroughSubject<Void, Never>()
    
        func dismiss() {
            dismissSubject.send()
        }
    }
    
  2. Add that to the view that will be addded to the UIHostingController:

    let dismissController = DismissController()
    
    dismissController.dismissSubject
        .sink { [weak self] in
            self?.dismiss(animated: true)
        }
        .store(in: &cancellables) // Ensure you manage subscriptions
        // e.g. var cancellables = Set<AnyCancellable>()
    let rootView = FirstView()
        .environmentObject(dismissController) // environmentObject is being created here
    
    let hostingController = UIHostingController(rootView: rootView)
    present(hostingController, animated: true)
    
  3. Make a reference to the environment object and perform the dismiss method where you desire:

    struct SecondView: View {
        @Environment(\.dismiss) var dismiss
        @EnvironmentObject var dismissController: DismissController
    
        // ...
        Button("OK") {
            dismiss() // Dismiss the SwiftUI sheet
            dismissController.dismiss() // Trigger dismissal of the hosting controller
        }
    }
    

Comments

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.