I want to send a user to a specific ViewController in my app once a notification is clicked.
I now that I can do something like this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewControllerWithIdentifier("Home") as? HomeViewController
presentedVC?.presentViewController(destinationViewController!, animated: true, completion: nil)
But my app has a tab bar and looks like this
Tab bar
tab1: navigationController -> VC1
tab2: navigationController -> VC2 -> HomeVC
tab: navigationController -> VC3
Each tab has a navigationController as a infront of it.
So how can I send the user to HomeVC? I must first select tab 2 then the navigation controller then push the user tvice:
tab2: navigationController -> VC2 -> HomeVC
And the other problem, if there any way to tell if the user is already in HomeVC? I dont want to send the user to the same VC if his already there.
You must have access to your UITabbarController in you UIApplicationDelegate or wherever you're handling the notification tap.
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController //or whatever your way of getting reference is
So first you'll get the reference to UINavigationController in your second tab like this:
let navInTab:UINavigationController = tabBar.viewControllers?[1] as! UINavigationController
Now push your home view at second tab's navigation controller:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewControllerWithIdentifier("Home") as? HomeViewController
navInTab.pushViewController(destinationViewController!, animated: true)
And finally switch your tab to second to show the just pushed home controller
tabBar.selectedIndex = 1
Keep in mind, this answer assumes that your application has already set the tab bar as the root view controller of application window prior handling the notification tap.
Try something like this:
if let tabBarController = window?.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 1 // in your case the second tab
}
The idea is to switch to get the tab bar instance and switch it to your desired tab (where you have your view controller).
The above code works in AppDelegate / you can easily call it anywhere by getting the tabBarController instance.
You can check which tab is selected by user with the method var selectedIndex: Int. You can check which view controller is present like this self.navigationController?.presentingViewController?.presentedViewController. This will solve your problem.
Related
Sorry, if it is an essential question.
The scheme is the following:
enter image description here
I want the following:
User taps last Tab
User goes to some ViewController (different from ViewController, actually connected with last Tab)
How to do this (without using segue)?
Thanks a lot in advance!
You can go with instantiateViewController with push when load your tab and push one viewController to another like below:
let next = self.storyboard?.instantiateViewController(withIdentifier: "nextVC")as! nextVC
self.navigationController?.pushViewController(next, animated: true)
You can assign ViewController in you UITabBarController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// create view controllers from storyboard
// Make sure you set Storyboard ID for both the viewcontrollers in
// Interface Builder -> Identitiy Inspector -> Storyboard ID
let clockViewController = storyboard.instantiateViewControllerWithIdentifier("ClockViewController")
let stopWatchViewController = storyboard.instantiateViewControllerWithIdentifier("StopWatchViewController")
// Set up the Tab Bar Controller to have two tabs
let tabBarController = UITabBarController()
tabBarController.viewControllers = [clockViewController, stopWatchViewController]
// Make the Tab Bar Controller the root view controller
window?.rootViewController = tabBarController
Im trying to open a certain view from a push notification but i keep losing the nav bar and the back and next references. this what my storyboard looks like this (with the view i want to open)
this is what i have in my AppDelagate:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController
self.window?.rootViewController = vc
What you're doing is completely replacing the root view controller of your application, this means that all the current UI will be discarded.
What you should do instead is to use your knowledge of your application to direct it to the new content. For example, if your root view controller is a navigation controller, you can cast rootViewController to a nav controller and push it (this will fail if your root view controller is something else, like a tab bar controller).
guard let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController else {
fatalError("Main Storyboard doesn't have a notification controller")
}
guard let nav = self.window?.rootViewController as? UINavigationController else {
return //handle unexpected state
}
nav.push(vc, animated: true)
Another option would be to embed your notification controller into a navigation controller, add a Close button, and present it modally, that way you can present it on top of rootViewController no matter what that controller is.
As we can see in the screenshot your provided, the application's root view controller is the UINavigationController instance.
And according to that, let me offer the next code:
func handleNotification(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "notification") as? NotificationViewController else{
debugPrint("NotificationViewController with identifier 'notification' not found")
return
}
guard let navVC = self.window?.rootViewController as? UINavigationController else{
debugPrint("RootViewController is not an UINavigationController")
return
}
navVC.pushViewController(vc, animated: true) //perhaps your will prefer to use false
}
Beside that, you can use more flexible implementation.
In your AppDelegate post a (NS)Notification when notification intercepted, the relevant view-controller(s) observe the notification, and act when notification broadcasted.
You can also set an identifier to the segue and invoke performSegue method from the observing view-controller
You can set from storyboard -> add view controller -> Embed in navigation controller -> set second view controller -> Attach seque between that controllers. You will see same view controllers like that image .
So i've implemented universal link in my app and when pressing on a button I open my second app with :
UIApplication.shared.open(aUrl!)
also I'm getting the call in :
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {}
which is an alert.
The question is like the title says..how could I navigate to a specific VC ,(from the FirstApp to the Second which i opened it with universal link), more like mapping a navigation controller stack .
I've writed something like :
if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyAdsController") as? MyAdsViewController {
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentedController = currentController.presentedViewController {
currentController = presentedController
}
currentController.present(controller, animated: true, completion: nil)
}
}
but it's an ugly representation, i need something like the hole navigation stack...
And also , can i make a helper class for this if i want to handle more with universal link? Any suggestion are appreciated.
You need to use the .viewControllers property of your UINavigationController you can do this using setViewControllers(_:animated:) method or modifying directly .viewControllers property where rootViewController will be the viewControllersArray[0] and topViewController will be viewControllersArray[count-1]
This property description and details can be founded in the UINavigationController documentation
viewControllers
Property
The view controllers currently on the navigation stack.
Declaration
SWIFT
var viewControllers: [UIViewController]
Discussion The root view controller is at index 0 in the array, the back view controller is at index n-2, and the top controller is at
index n-1, where n is the number of items in the array.
Assigning a new array of view controllers to this property is
equivalent to calling the setViewControllers:animated: method with the
animated parameter set to false.
example
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let firstViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController") as? FirstViewController
let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
let thirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as? ThirdViewController
self.navigationController?.setViewControllers([firstViewController,secondViewController,thirdViewController], animated: false)
Alright so this is less of a Universal Link question and more of a navigation preference. When you are passed a universal link in your continueUserActivity function you should parse it and navigate accordingly from your app delegate. You should really switch your Universal Link handling to Branch since they will handle this for you for free and they pass metadata through a callback instead of link parameters which will make your links more powerful. Anyways...
Start with a UINavigationViewController
This consists of having a rootViewController which should be an instance of your main page. Store this navigation view controller as an instance variable so your app is only dealing with one Navigation view controller on launch. If you're using a storyboard then you can just just get reference to that navigation view controller:
self.nav = self.window?.rootViewController as? UINavigationViewController
Pushing vs presenting a View Controller onto Nav
When you receive a link and determined that you need to bring that user to a new page, you should decide whether to push or present the VC.
Pushing
Pushing is necessary if you would like to maintain that view as part of a stack. For example, if you want to show the user a pair of shoes, you may want to take them into the shoes category, then present them with the shoe detail controller. In this case you could take the user to the home page and push the proper navigation stack onto your UINavigationViewController.
self.nav.popToRootViewController(animated: true)
self.nav.pushViewController(firstController, animated: true)
self.nav.pushViewController(secondViewController, animated: true)
This way your users UINavigationController stack will look like root > firstVC > secondVC and they will have bread crumbs to be able to traverse back through the flow.
Presenting
In the case that you do not want to push a view onto the stack. Maybe you want to present a popup regardless of where they are at in the app and you don't want to mess up their location, you should use present a VC. This will simply present a ViewController over the entire NavigationController without being added to the NavigationController's stack.
self.nav.presentViewController(modalVC, animated:true, completion: nil)
From within that modalVC you can call
self.dismiss(animated: true, completion: nil)
In my app I am making the account page the new root VC when a user logs in.
It looks like this:
Navigation controller -> table view -> menu(modal segue) -> login screen(modal segue) -> account page
When transitioning from login to account I am using:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let vc = storyboard.instantiateViewControllerWithIdentifier("testVc")
let navigationController = self.view.window?.rootViewController as! UINavigationController
navigationController.setViewControllers([vc], animated: true)
This makes the account page the new root VC. But the only problem is that once is shows up both the menu and login form is still visible ontop of the screen.
So how do I clear two old VC's shown as modal?
Update got it to work using:
#IBAction func loginButtonDidTouch(sender: AnyObject) {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("testVc")
let navigationController = self.view.window?.rootViewController as! UINavigationController
self.presentingViewController!.presentingViewController!.dismissViewControllerAnimated(false) { () -> Void in
navigationController.setViewControllers([vc], animated: true)
}
}
But I dont know if this is the right way to do it?
You need to get the reference of those controllers, and then dismissing them.
Try this:
let loginScreen = self.window.rootViewController.presentedViewController
loginScreen.dismissViewControllerAnimated(false) { () -> Void in
let menuScreen = self.window.rootViewController.presentedViewController
menuScreen.dismissViewControllerAnimated(false, completion: nil)
}
When you're calling the original navigation stack and modifying it:
let navigationController = self.view.window?.rootViewController as! UINavigationController
You are setting the new view controller (#testVc) by replacing the only other view controller, "tableview", in that navigation stack.
The modally presented views are not a part of that particular navigation stack and instead are presented above the current navigation stack as new stacks (this gives you a pointer to the new Navigation Controller on top in the form of self.navigationController to push new views)
You can explicitly dismiss the two modally presented views by calling dismissViewControllerAnimated(_:completion:) on each, most likely by propagating the communication through a delegate response or through the completion handler.
This is my storyboard:
Short Description:
My App starts with the LaunchController
a modal segue shows the Reveal View Controller
this bring the Menu Controller and my Main Navigation Controller (ID
"NavController"; green Navbar) together. this will create a slide
menu. (Basic Code:
appcoda.com/ios-programming-sidebar-navigation-menu/)
my Main Navigation Controller shows a TableViewController.
this one have a menu button (3 Lines) which make the slide menu
visible
the plus icon willo push the last view Controller (ID: "VC1").
My Problem:
I would like to set quick actions for my app.
This code help works for that:
#available(iOS 9.0, *)
func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
var succeeded = false
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("NavController")
self.window?.rootViewController = initialViewController
let navVC = self.window?.rootViewController as! UINavigationController
switch shortcutItem.type {
default:
navVC.pushViewController(storyboard.instantiateViewControllerWithIdentifier("VC1"), animated: false)
succeeded = true
break
}
return succeeded
}
This Code set the NavController as initial Controller und push the to VC1.
This works fine.
With the X icon in VC 1, i use the unwind function back to TableView.
The Problem: if i use the undwind function and fall back to tableview i can't open the slide menu. A Touch on the menu icon give no reaction.
The Problem can be, that i start the app with the quick action behind the Reveal View Controller.
How can I solve this problem?
Did you try to make the reveal view controller the entry point of your app and deleting the launch controller, because if you just want to go back to the main view you could just do this :
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateInitialViewController() as UIViewController!
self.presentViewController(controller, animated: false, completion: nil)
If you cannot could you explain the purpose of the launch controller?