代码之家  ›  专栏  ›  技术社区  ›  ap123

在AppDelegate中打开ViewController,同时保留选项卡栏

  •  2
  • ap123  · 技术社区  · 7 年前

    在我的Xcode项目中,当用户点击通知时,我想首先将其发送到选项卡栏中的某个项目,然后我想实例化一个视图控制器,并将一个对象发送到该视图控制器。我有代码将它们发送到我想要的选项卡栏,但我不知道如何将它们实例化到视图控制器,同时保持选项卡栏和导航栏连接到视图控制器。关于这一点的所有答案都需要您更改根视图控制器,这会使我在调用视图控制器时失去与选项卡栏和导航栏的连接。

    这是一个真实的例子: 用户收到Instagram通知,说“John开始跟踪你”->用户点击通知->Instagram打开并显示通知选项卡->快速将用户发送到“John”配置文件,当用户按下后退按钮时,它会将用户发送回通知选项卡

    应该知道:我之所以要首先访问某个选项卡,是为了获取该选项卡的导航控制器,因为我要访问的视图控制器没有导航控制器。

    以下是我将用户发送到“通知”选项卡的工作代码(为了更好地理解,我添加了类似Instagram示例的注释):

    if let tabbarController = self.window!.rootViewController as? UITabBarController {
        tabbarController.selectedViewController = tabbarController.viewControllers?[3] //goes to notifications tab
        if type == "follow" { //someone started following current user                            
            //send to user's profile and send the user's id so the app can find all the information of the user                    
        }
    }
    
    3 回复  |  直到 5 年前
        1
  •  7
  •   Mannopson    7 年前

    首先,您将无法满足TabBarController:

    let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
    let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
    

    然后贪得无厌的 viewControllers UINavigationController ? 如果是这样,您将改为不满足导航控制器:

    let first = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
    let second = storyboard.instantiateViewiController(withIdentifier: "YourSecondNavigationController") as! UINavigationController
    let third = storyboard.instantiateViewiController(withIdentifier: "YourThirdNavigationController") as! UINavigationController
    

    此外,还应实例化所需的ViewController:

    let desiredVC = storyboard.instantiateViewController(withIdentifier: "desiredVC") as! ExampleDesiredViewController
    

    将所有导航控制器设置为 视图控制器 TabBarController的:

    tabBarController.viewControllers = [first, second, third]
    

    if tabBarController.selectedViewController == first {
    
    // Option 1: If you want to present
    first.present(desiredVC, animated: true, completion: nil)
    
    // Option 2: If you want to push
    first.pushViewController(desiredVC, animated. true)
    
    }
    

    将tabBarController设置为 rootViewController :

    self.window = UIWindow.init(frame: UIScreen.main.bounds)   
    self.window?.rootViewController = tabBarController
    self.window?.makeKeyAndVisible()
    

    最后:这是您完成的代码:

    func openViewController() {
    
    let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
    let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
    
    let first = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
    let second = storyboard.instantiateViewiController(withIdentifier: "YourSecondNavigationController") as! UINavigationController
    let third = storyboard.instantiateViewiController(withIdentifier: "YourThirdNavigationController") as! UINavigationController
    
    let desiredVC = storyboard.instantiateViewController(withIdentifier: "desiredVC") as! ExampleDesiredViewController
    
    tabBarController.viewControllers = [first, second, third]
    
    if tabBarController.selectedViewController == first {
    
    // Option 1: If you want to present
    first.present(desiredVC, animated: true, completion: nil)
    
    // Option 2: If you want to push
    first.pushViewController(desiredVC, animated. true)
    
    }
    
    self.window = UIWindow.init(frame: UIScreen.main.bounds)   
    self.window?.rootViewController = tabBarController
    self.window?.makeKeyAndVisible()
    
    }
    

    如果要在点击通知时显示或推送ViewController?试试这样:

    extension AppDelegate: UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    
            switch response.actionIdentifier {
            case UNNotificationDefaultActionIdentifier:
                openViewController()
                completionHandler()
    
            default:
                break;
            }
        }
    }
    
        2
  •  2
  •   Alex Wally    7 年前

    我可以想出两种方法:

    1) 如果该视图控制器是UINavigationController,则您可以从任何位置推送配置文件:

    if let tabNavigationController = tabbarController.viewControllers?[3] as? UINavigationController {
        tabbarController.selectedViewController = tabNavigationController
        let profileViewController = ProfileViewController(...)
        // ... set up the profile by setting the user id or whatever you need to do ...
        tabNavigationController.push(profileViewController, animated: true)    // animated or not, your choice ;)
    }
    

    2) 或者,我喜欢直接从视图控制器子类(在本例中为PostListViewController)控制这些事情。我在swift文件中有这个助手方法,我将其包含在我的所有项目中:

    extension UIViewController {
        var containedViewController: UIViewController {
            if let navController = self as? UINavigationController, let first = navController.viewControllers.first {
                return first
            }
            return self
        }
    }
    

    然后我会这样做来推动新的视图控制器:

    if let tabViewController = tabbarController.selectedViewController {
        tabbarController.selectedViewController = tabViewController
        if let postListViewController = tabViewController.containedViewController as? PostListViewController {
            postListViewController.goToProfile(for: user)    // you need to get the user reference from somewhere first
        }
    }
    
        3
  •  1
  •   Glenn Posadas    7 年前

    在我上一个live项目中,我使用了与您相同的方法。因此,尽管我怀疑这种方法是否正确或理想地处理来自AppDelegate的推送通知(我在iOS中仍有很多东西需要学习),但我仍在分享它,因为它适合我,而且我相信代码仍然可读且相当干净。

    关键是要知道屏幕的级别或堆叠。什么是childViewControllers,最上面的屏幕,最下面的屏幕,等等。。。

    然后,如果您现在准备推到某个屏幕,那么您当然需要当前屏幕的navigationController。

    例如,此代码块来自我的项目的AppDelegate:

        func handleDeeplinkedJobId(_ jobIdInt: Int) {
            // Check if user is in Auth or in Jobs
            if let currentRootViewController = UIApplication.shared.keyWindow!.rootViewController,
                let presentedViewController = currentRootViewController.presentedViewController {
                if presentedViewController is BaseTabBarController {
                    if let baseTabBarController = presentedViewController as? BaseTabBarController,
                        let tabIndex = TabIndex(rawValue: baseTabBarController.selectedIndex) {
                        switch tabIndex {
                        case .jobsTab:
    ....
    
    ....
    
                        if let jobsTabNavCon = baseTabBarController.viewControllers?.first,
                            let firstScreen = jobsTabNavCon.childViewControllers.first,
                            let topMostScreen = jobsTabNavCon.childViewControllers.last {
    
    ...
    ...
    

    正如你们所见,我知道屏幕的层次结构,通过使用这些知识以及一些耐心来检查我是否在正确的屏幕上 断点 printobject (po) ,我得到了正确的参考。最后,在上面的代码中,我有 topMostScreen 引用,如果我想的话,我可以使用该屏幕的navigationController推送到一个新屏幕。

    希望这有帮助!