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

Posted

技术标签:

【中文标题】单击推送通知时,正确重定向到选项卡栏导航控制器内的视图控制器【英文标题】:Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked 【发布时间】:2018-08-09 08:52:33 【问题描述】:

我有一个tabBar。它的每个选项卡的ViewControllers 都嵌入在它们各自的NavigationControllers 中。

层次结构:

Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for 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 中相同的层次结构?

编辑:

下图显示了我的布局。

我想完成以下工作:

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

    如果应用程序已被终止并且用户点击通知,它应该打开 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 是否位于导航堆栈的顶部。

并且,在应用程序终止的情况下,单击推送通知会打开标签栏,并选择默认选择索引。

我花了几天时间试图解决这个问题。但不能让它工作。

【问题讨论】:

你为什么要设置rootview控制器为什么不简单推送 当你从推送通知进入应用时,有一种可能,你不在你想去的地方,所以推送并不总是一个合适的选择 【参考方案1】:

我想你必须这样做:

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)
    
 

【讨论】:

未使用此代码打开所需的视图控制器。 @Sujal 所以,你有UITabBarController,它有几个标签。每个选项卡都有UINavigationController 作为根。您想在特定选项卡上打开 ChatViewController(例如选项卡索引 1 )。对?如果是这样,1)将标签栏切换到右侧标签tabBar.selectedIndex = 0; 2)在当前选项卡上获取navigationController(就像我一样); 3) 创建ChatViewController 的新实例并将其推送到navigationController。您可以在此代码上设置断点并检查是否所有此对象都存在。 所有条件都通过了,但是已经推送的viewcontroller没有显示出来。它只是打开应用程序之前留下的选项卡。附:在 tabBarController (用于登录)之前,我还有几个视图控制器。这应该没什么区别吧? 对。如果您对 tabBarController 的显式访问(在应用程序激活时出现在屏幕上)它应该可以工作。这个对我有用。您可能有其他原因导致它无法正常工作。 @AndrewBogaevskyi - 这是几年后的救命稻草 - 效果很好!【参考方案2】:

嗯,你正在设置当前的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

【讨论】:

【参考方案3】:

从标签栏控制器获取当前导航控制器。你可以使用这个功能

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
    

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

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)

【讨论】:

代码似乎有效,但我有两个担忧。 1.如果应用程序终止后点击通知,应用程序没有打开所需的VC 2.当用户在同一个VC并点击推送通知时,我想留在同一个VC而不是在堆栈中创建一个新的如何解决这些问题,尤其是第一个?【参考方案4】:

您可以在 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()
    
    

【讨论】:

以上是关于单击推送通知时,正确重定向到选项卡栏导航控制器内的视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

单击通知时更改选项卡栏

单击时,Phonegap 推送通知重定向到应用程序中的特定页面

检测应用程序是不是从推送通知启动/打开,然后将其重定向到特定视图控制器

当点击 FCM 推送通知时,如何在选项卡栏中打开特定的视图控制器?

在 AppDelegate iOS 中单击 FCM 推送通知时导航到特定选项卡 - Swift

Flutter - 推送通知单击重定向到特定屏幕不起作用