SwiftUI - Presenting alerts, dialogs from Views

993 views Asked by At

I created an application in SwiftUI.

Views are structs, but I need to know the View Controller because it is needed for presenting some special alerts.

I see that in my project there are AppDelegate, and SceneDelegate.

I found this

let contentView = ContentView()

    // Use a UIHostingController as window root view controller.
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }

So I want to pass the View Controller reference to the other Views.

Is window.rootViewController the right value to pass and use?

2

There are 2 answers

0
P5music On

I do not know if this answer is a suitable solution, but it simply works for me, so be careful when using it.

class func getTopMostViewController() -> UIViewController? {
    var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

    while let presentedViewController = topMostViewController?.presentedViewController {
        topMostViewController = presentedViewController
    }

    return topMostViewController
}

the use the value like

let viewController=getTopMostViewController()
viewController.present(...

from StackOverflow answer:

Get top most UIViewController

1
Asperi On

The possible solution is to inject hosting view controller as environment key, so it can be available at any ContentView internal hierarchy level.

Here is a demo. Tested with Xcode 12 / iOS 14.

  1. Declare environment key
struct RootViewControllerKey: EnvironmentKey {
    static let defaultValue: UIViewController? = nil
}

extension EnvironmentValues {
    var rootViewController: UIViewController? {
        get { self[RootViewControllerKey.self] }
        set { self[RootViewControllerKey.self] = newValue }
    }
}
  1. Inject environment into ContentView
if let windowScene = scene as? UIWindowScene {
    let window = UIWindow(windowScene: windowScene)

    let rootController = UIHostingController(rootView: AnyView(EmptyView()))
    rootController.rootView = AnyView(contentView
        .environment(\.rootViewController, rootController))
    window.rootViewController = rootController

    self.window = window
    window.makeKeyAndVisible()
}
  1. Use inside
struct ContentView: View {
    // can be used here as well

    var body: some View {
      TestSubView()
    }
}

struct TestSubView: View {
    @Environment(\.rootViewController) var viewController // for demo here!!

    var body: some View {
      Text("Demo")
        .onAppear {
          print(String(describing: self.viewController))
        }
    }
}