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



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

一个真实的例子:用户收到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                    
}
}

首先,您需要一个TabBarController:

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

然后对TabBarController的所有CCD_ 1进行贪得无厌。如果您的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:的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;
}
}
}

我可以想出两种方法来做到这一点:

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,最上面的屏幕,在底部的屏幕,等等…

然后,如果你现在准备推到某个屏幕,你当然需要当前屏幕的导航控制器。

例如,这个代码块来自我项目的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参考,如果我想的话,我可以使用该屏幕的导航控制器推送到新屏幕

希望这能有所帮助!

最新更新