问题
I have a tabBar
. Each of its tabs' ViewControllers
have been embedded inside their respective NavigationControllers
.
Hierarchy:
Tabbarcontroller
--> NavigationController
--> VC1 --> VC2 -->...VCn (for tab1)
I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.
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
}
By using this, I am redirected to the respective ViewController
. However, i am not able to get back to the tabBar
. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?
EDIT:
The image below shows my layout.
I would like to accomplish the following:
When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.
If the app had been terminated and the user taps on the notification, it should open VC-B.
For determining if the app had been terminated, I set a flag as:
func applicationWillTerminate(_ application: UIApplication) {
UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}
This flag is set false at the end of didFinishLaunchingWithOptions function.
For redirection, I check this flag to determine if the app had been terminated:
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)
}
}
}
}
With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.
And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.
I have spent several days trying to fix this. But cannot get it work.
回答1:
I suppose you have to do something like this:
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:
Well, you are setting current window.rootViewController
, which should be that TabBarViewController
I'd say. That's why you cannot get back to the TabBar
What you should do is
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:
To get the current navigation controller from the tabbar controller. You can use this function
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
}
Using the function you can navigate your viewcontroller like below.
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)
回答4:
You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.
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
}
}
}
Then you can call that method on your AppDelegate like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let application = UIApplication.shared
AppDelegate.shared.rootViewController.showDetailsScreen()
completionHandler()
}
}
来源:https://stackoverflow.com/questions/51762691/properly-redirect-to-a-view-controller-inside-tab-bars-navigation-controller-wh