Skip to content

Instantly share code, notes, and snippets.

@Kanishka3
Last active December 13, 2021 11:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kanishka3/edcfd6d5b691f34b1756c7b82b24369a to your computer and use it in GitHub Desktop.
Save Kanishka3/edcfd6d5b691f34b1756c7b82b24369a to your computer and use it in GitHub Desktop.
// NavigationControllerSwiftUI.swift
// Created by Kanishka on 17/11/21.
// More: https://bio.link/kanishka
import SwiftUI
import UIKit
// Reference: https://stackoverflow.com/a/67495147/7992741
struct NavigationController {
/// Pops the current view to the root view
static func popToRootView() {
findNavigationController(viewController: UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController)?
.popToRootViewController(animated: true)
}
/// Pushes a view controller on top of the current view
static func pushController(_ controller: UIViewController){
findNavigationController(viewController: UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController)!.pushViewController(controller, animated: true)
}
/// Allows a function to do actions with the current navigation controller
static func navigationAction(_ action: ((UINavigationController)->())) {
action(findNavigationController(viewController: UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController)!)
}
/// Goes through the application window to find the navigation controller
static private func findNavigationController(viewController: UIViewController?) -> UINavigationController? {
guard let viewController = viewController else {
return nil
}
if let navigationController = viewController as? UINavigationController {
return navigationController
}
for childViewController in viewController.children {
return findNavigationController(viewController: childViewController)
}
return nil
}
}
extension UIApplication {
var keyWindow: UIWindow? {
return UIApplication.shared.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first(where: { $0 is UIWindowScene })
.flatMap({ $0 as? UIWindowScene })?.windows
.first(where: \.isKeyWindow)
}
}
@Kanishka3
Copy link
Author

Kanishka3 commented Nov 17, 2021

Sometimes, it is hard to call methods on NavigationView in SwiftUI and that is when we start missing the UIKit features but here is the class which works like magic and relies on UIAppication window and lets you do everything that one could do with UIKit UINavigaitonController.

@rudrankriyam
Copy link

Do you have an example of this?

@Kanishka3
Copy link
Author

Yep! If you wanna pop to root view or trigger pushController without clicking a button (maybe you are performing a .task {} ) , this is a better option.

PS: I used this for pushing controller in SwiftUI views when shareplay activity was recognized.

@Kanishka3
Copy link
Author

struct FirstView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: SecondView()){
                Text("Go to another view")
            }
        }
    }
}

struct SecondView : View {
    var body: some View {
        Button("Go to root view") {
                        // popping to root without EnvironmentObject
            NavigationController.navigationAction { navigationController in
                navigationController.popToRootViewController(animated: true)
            }
        }.onAppear {
        // triggering a push or pop to root based on a timer or urlsession completion or async task
            Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
                NavigationController.navigationAction { controller in
                    var view = Color.red
                    var viewController = UIHostingController(rootView: view)
                    controller.pushViewController(viewController, animated: true)
                }
            }
        }
        
    }
}

@rudrankriyam
Copy link

Thanks! Do you think we can use this without NavigationView? and have a UINavigationController as the main controller that handles the FirstView?

@Kanishka3
Copy link
Author

Yep, it works with that too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment