Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked

痴心易碎 提交于 2021-02-07 14:35:12

问题


I have a tabBar. Each of its tabs' ViewControllers have been embedded inside their respective NavigationControllers.

Hierarchy:

Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for tab1)

I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.

let navigationController = UINavigationController()
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
    navigationController.viewControllers = [chatViewController]
    window?.rootViewController = navigationController
}

By using this, I am redirected to the respective ViewController. However, i am not able to get back to the tabBar. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?

EDIT:

The image below shows my layout.

I would like to accomplish the following:

  1. When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.

  2. If the app had been terminated and the user taps on the notification, it should open VC-B.

For determining if the app had been terminated, I set a flag as:

func applicationWillTerminate(_ application: UIApplication) {
    UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}

This flag is set false at the end of didFinishLaunchingWithOptions function.

For redirection, I check this flag to determine if the app had been terminated:

func performRedirectionToSuitableViewController(userInfo: [AnyHashable: Any]) {
  
    let isTerminated = UserDefaults.standard.object(forKey: UserDefaultsKey.isTerminated) as! Bool
    
    if isTerminated {
        
        let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
        
        let tab = storyboard.instantiateViewController(withIdentifier: "tabBar") as! UITabBarController
        
        tab.selectedIndex = 0
        
        let nav = tab.viewControllers![0] as! UINavigationController
        
        let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
        
        chatViewController.chatThreadId = userInfo["thread_id"] as? String
        
        nav.pushViewController(chatViewController, animated: true)

    } else {

        if let tab = window?.rootViewController?.presentedViewController as? UITabBarController {

            let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)

            let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
                
            chatViewController.chatThreadId = userInfo["thread_id"] as? String
               
            if let nav = tab.selectedViewController as? UINavigationController {
                   
                nav.pushViewController(chatViewController, animated: true)
            }     
        }
    }
}

With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.

And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.

I have spent several days trying to fix this. But cannot get it work.


回答1:


I suppose you have to do something like this:

let tabBar: UITabBarController // get your tab bar
tabBar.selectedIndex = 0 // eg. zero. To be sure that you are on correct tab
if let navigation = tabBar.viewControllers?[tabBar.selectedIndex] as? UINavigationController {
    let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
    if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
        navigation.pushViewController(chatViewController, animated: true)
    }
 }



回答2:


Well, you are setting current window.rootViewController, which should be that TabBarViewController I'd say. That's why you cannot get back to the TabBar

What you should do is

let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController, let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar") as? UITabBarController {
    let navigationController = UINavigationController(rootViewController: chatViewController)
    navigationController.viewControllers = [chatViewController]
    tabBar.viewControllers = [navigationController]
    window?.rootViewController = tabBar
}



回答3:


To get the current navigation controller from the tabbar controller. You can use this function

func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
        
        var rootVC = rootViewController
        if rootVC == nil {
            rootVC = UIApplication.shared.keyWindow?.rootViewController
        }
        
        if rootVC?.presentedViewController == nil {
            return rootVC
        }
        
        if let presented = rootVC?.presentedViewController {
            if presented.isKind(of: UINavigationController.self) {
                let navigationController = presented as! UINavigationController
                return navigationController.viewControllers.last!
            }
            
            if presented.isKind(of: UITabBarController.self) {
                let tabBarController = presented as! UITabBarController
                return tabBarController.selectedViewController!
            }
            
            //UIAlertController
            if presented.isKind(of: UIAlertController.self) {
                let alertController = presented as! UIAlertController
                return alertController.presentingViewController
            }
            return getVisibleViewController(presented)
        }
        return nil
    }

Using the function you can navigate your viewcontroller like below.

let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                let yourView = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourView") as! NewnoteViewController

                let navi = self.getVisibleViewController(self.window!.rootViewController) as! UINavigationController
               
                navi.pushViewController(yourView, animated: true)



回答4:


You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.

class RootViewController: UIViewController {
    private var currentView: UIViewController
    
    init() {
        self.currentView = ViewController()
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addChildViewController(currentView)               // 1
        currentView.view.frame = view.bounds              // 2
        view.addSubview(currentView.view)                 // 3
        currentView.didMove(toParentViewController: self) // 4
    }
    
    func showDetailsScreen() {
        if let updateDetailView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UpdateDetailsNotif") as? UpdateDetailsViewController{
            
            let navController = UINavigationController(rootViewController: updateDetailView)

            addChildViewController(navController)
            navController.view.frame = view.bounds
            view.addSubview(navController.view)
            navController.didMove(toParentViewController: self)
            currentView.willMove(toParentViewController: nil)
            currentView.view.removeFromSuperview()
            currentView.removeFromParentViewController()
            currentView = navController
        }
        
    }
   
}

Then you can call that method on your AppDelegate like this:

  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let application = UIApplication.shared
  
        AppDelegate.shared.rootViewController.showDetailsScreen()
       
        completionHandler()
    
    }
}


来源:https://stackoverflow.com/questions/51762691/properly-redirect-to-a-view-controller-inside-tab-bars-navigation-controller-wh

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!