单击推送通知后,正确重定向到选项卡栏导航控制器内的视图控制器

时间:2018-08-09 08:52:34

标签: ios swift push-notification uinavigationcontroller uitabbarcontroller

我有一个tabBar。每个选项卡的ViewControllers均已嵌入各自的NavigationControllers中。

层次结构:

Tabbarcontroller-> NavigationController-> VC1-> VC2-> ... VCn(用于tab1)

当我单击推送通知时,我必须重定向到一个说VC2的视图控制器。我正在使用的代码如下。

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
}

使用此方法,我被重定向到相应的ViewController。但是,我无法回到tabBar。那么是否可以通过这样的方式进行重定向,使其允许我回到标签栏,从而提供与UI中相同的层次结构?

编辑:

下图显示了我的布局。

enter image description here

我想完成以下任务:

  1. 当用户点击推送通知时,应将应用程序定向到VC-B。 *如果VC-B已经在导航堆栈的顶部,则不应为VC-B创建新对象并将其添加到导航堆栈。

  2. 如果该应用已终止并且用户点击通知,则应打开VC-B。

为确定应用程序是否已终止,我将标志设置为:

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

在didFinishLaunchingWithOptions函数的末尾将此标志设置为false。

对于重定向,我检查此标志以确定应用是否已终止:

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)
            }     
        }
    }
}

使用此代码,部分满足了我的第一个要求。需要确定viewcontroller是否在导航堆栈的顶部。

并且,对于终止的应用程序,单击推送通知将打开选项卡栏,并选择默认的选择索引。

我花了几天的时间来解决这个问题。但是无法正常工作。

4 个答案:

答案 0 :(得分:0)

好吧,您正在设置当前的window.rootViewController,我应该说是TabBarViewController。这就是为什么您无法回到TabBar

您应该做的是

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
}

答案 1 :(得分:0)

我想你必须做这样的事情:

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 :(得分:0)

从选项卡控制器获取当前的导航控制器。您可以使用此功能

override

使用该功能,您可以像下面那样浏览视图控制器。

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
    }

答案 3 :(得分:0)

您可以在RootViewController中创建一个方法,该方法将在收到推送通知后将用户重定向到特定视图。这是我在上一个项目中所做的。

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
        }
        
    }
   
}

然后,您可以像这样在AppDelegate上调用该方法:

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