在保持 Tabbar 的同时在 AppDelegate 中打开 ViewController

Opening ViewController In AppDelegate While Keeping Tabbar

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

一个真实的例子: 用户收到 Instagram 通知说 "John started following you" -> 用户点击通知 -> Instagram 打开并显示通知选项卡 -> 快速发送用户到 "John" 个人资料,当用户按下后退按钮时,它会将他们发送回通知选项卡

应该知道:我之所以先去某个tab是为了获取那个tab的navigation controller,因为我要去的view controller没有。

这是我将用户发送到 "notifications" 选项卡的工作代码(为了更好地理解,我添加了评论以类似于 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                    
    }
}

我可以想到两种方法:

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

在我上一个现场项目中,我使用了与您相同的方法。因此,即使我怀疑这种方法是否正确或理想地处理来自 AppDelegate 的推送通知(我在 iOS 中还有很多东西要学习),我仍然分享它,因为它对我有用并且好吧,我相信代码仍然可读并且非常干净。

关键是了解屏幕的层数或层数。什么是 childViewControllers、topMost 屏幕、底部屏幕等等...

然后,如果您现在准备推送到某个屏幕,您当然需要当前所在屏幕的 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 推送到新屏幕。

希望对您有所帮助!

首先,您需要设置一个 TabBarController:

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

然后填满 TabBarController 的所有 viewControllers。如果您的 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

将所有 NavigationController 设为 TabBarController 的 viewControllers

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