How to dismiss any popover viewcontrollers when activated in iOS? - ios

I have some ViewControlls for settings, info etc.
Users can close the app settings ViewController open(I mean popover the ViewController).
When local notification is received I want the the app go to the root viewcontroller and dismiss any popovers.

EDIT
this answer is only good if the VC you are trying to go back to does not need any special initialization, since this method creates a new instance of it. keep that in mind.
Try this method, it will remove anything in your stack of View Controllers and make a specific View Controller presented on screen:
func dismissAllAndNavigate(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let window = appDelegate.window
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootController = mainStoryboard.instantiateViewController(withIdentifier: "some identifier") as! UIViewController
window?.rootViewController = rootController
}
Just make sure that the name of the storyboard is correct and the identifier of the view controller in that storyboard is defined.

Related

Perform Segue from ViewController that isn't open

I have a background task that runs from AppDelegate, when it needs to it displays notifications.
When these notifications are tapped they should direct the user to the ViewController that relates to the notification.
I was wondering if it was possible to perform segues from AppDelagate.
My ViewControllers are in a navigation controller. I'm guessing I have to instantiate my root view controller and perform segue there, just not sure how to do that from appdelegate.
Edit:
Here is my code so far, it works it just isn't embedded in my navigation controller
if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ConversationVC") as? conversationTableViewController {
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)
}
}
You can't segue from a storyboard that isn't loaded yet.
Depending on your case you could:
1: Send data from the AppDelegate to the initial root view controller of your current storyboard and from there, create multiple segues to the respectable VC depending on the data.
OR
2: Create multiple storyboards (one for each case) and launch the appropriate ones from the AppDelegate according to the notification.
Edit: Need to see the storyboard. For the variable, add it to conversationTableViewController and set it up just before you present it:
controller.myVar = "someValue"
self.window.rootViewController = controller
self.window.makeKeyAndVisible()

Navigate to a specific View Controller inside a TabBarController from AppDelegate, Swift

I have a TabBarController with two tabs Cases & Settings
I would like to take the user to CaseSummaryTVC which is nested like this
TabBarController > Cases (NavigationController, Storyboard Id = 'tvcNav' ) > CasesTVC (TableViewController) > CaseSummaryTVC (TableViewController, Storyboard Id = 'CaseSummaryTVC').
I am using the below code in AppDelegate, which takes me to the 'CaseSummaryTVC' but doesn't show the TabBar on the bottom.
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navController = mainStoryboard.instantiateViewController(withIdentifier: "tvcNav") as! UINavigationController
let caseSummaryTVC = mainStoryboard.instantiateViewController(withIdentifier: "CaseSummaryTVC") as! CaseSummaryTVC
navController.pushViewController(caseSummaryTVC, animated: true)
self.window?.rootViewController = navController
self.window?.makeKeyAndVisible()
It looks like you're setting your window's rootViewController to be 'navController', which doesn't seem to be wrapped in a UITabBarController.
I'm not sure what your storyboard looks like, but it sounds like you have the view hierarchy setup correctly there.
You could do one of the following:
1) Remove the programmatic view controller instantiation and have the storyboard take care of setting the rootViewController to the UITabBarController.
2) Instantiate the UITabBarController programmatically like you have done here with the other view controllers, and make sure it's set up to have the navController as one of it's tabs.

Is there a way to assign the rootViewController of a NavigationController?

Is there a way to change root controller when someone is midway into using the app? The way I have it set up right now is that if a new user uses the app they will go into a login page that has the embedded navigation controller. When the user fills out the information they go onto the next page to confirm. The embedded navigation is to go back and change any info. Once they confirm they go to the main page. Now the issue is that the app goes to a different view that already has a Navigation Controller when the user returns and follows some logic. How do I assign the returning user navigation controller when the new user gets into that section? When the user presses back while they are on the blank view they will go back to the confirmation page.
How do I have the confirmation page view get the navigation controller on the top and not the bottom? I'm not sure where to change the Root Navigation Controller but I assume it should be in the prepareForSegue in Confirmation View Controller
if segue.identifier == "showMemoryTable" {
let memoryListVC = segue.destination as? ReturningUserCityDetailTableViewController
memoryListVC?.userData = userData
if userData?.newUser == true {
let newRootViewController = ReturningUserCityTableViewController()
self.navigationController?.viewControllers.first = newRootViewController
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "homeVC")
navigationController?.setViewControllers([viewController], animated: true)
}
I am not sure if I should use setViewControllers but I believe so
If you are aiming to Change the root view controller, you should not do it by by performing a segue, it would be more appropriate to do it like this:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newRootViewController = mainStoryboard.instantiateViewController(withIdentifier: "ReturningUserCityTableViewController") as! ReturningUserCityTableViewController()
let ad = UIApplication.shared.delegate as! AppDelegate
ad.window?.rootViewController = newRootViewController
Note that it changes the whole app root view controller; You could set the desired navigation controller to be the root of the app (obviously you could get them by their storyboard ID).

Segue to a UITabBarViewController after some create account questions

I have what I am calling a one time onboard set of UIViews that ask the user a couple questions to configure my iOS app correctly.
When the user has answered the onboarding questions I would like to segue to the UITabBarController Home screen, this is the same screen that will always appear every subsequent time the app opens after onboarding is complete. I do not have an Initial UIViewController set in my Storyboard, this, in my AppDelegate is how I go to the correct view, every time the app loads:
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if Settings.sharedInstance.onboardComplete{
let vc = storyboard.instantiateViewController(withIdentifier: baseTabBarStoryboardIdentifier) as! UITabBarViewController
self.window?.rootViewController = vc
} else{
self.window?.rootViewController = storyboard.instantiateViewController(withIdentifier: onboardStoryboardIdentifier) as! OnboardNavigationController
}
self.window?.makeKeyAndVisible()
This works fine, and I am able to load the UITabBarViewController which contains all my app's primary UIViews without any issues.
I would like to achieve this same "segue" from a UIButton TouchUpInside action. This segue would occur in a set of UIViews in a UINavigationViewController that has no connection to the main app UIViews contained in aforementioned UITabBarViewController, here is a screenshot of my Storyboard to give some context to this statement, you can see the onboarding UIViews circled in red, the main app UIViews contained in a UITabBarViewController are circled in blue:
I have tried the same code as above in my AppDelegate in my UIButton action outlet implementation, I create a UIWindow instance, I get the Storyboard, I instantiate the UITabBarViewController, I set the UIWindow rootViewController. I am not able to figure out a way to transition to my main app UITabBarViewController after the user completes the Onboarding questions
Try this
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeController = mainStoryboard.instantiateViewController(withIdentifier:baseTabBarStoryboardIdentifier) as! UITabBarViewController
appDelegate?.window?.rootViewController = homeController

Best way to conditionally switch root view controller at iOS app start-up

New iOS developer here. I am working on a project which requires that the user log-in when they first open the app. From then on, I want the app to open directly to the main flow of the app (a tab bar controller in my case). After doing some research, I have come across what seems to be two main ways to do implement this functionality:
1) Conditionally set the root view controller of the app's window in the app delegate. For example:
if userLoggedIn {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = tabBarController
} else {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let logInViewController: LogInViewController = storyboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = logInViewController
}
2) Use a navigation controller as the app's root view controller, and conditionally set the view controllers managed by the navigation controller in the app delegate. For example:
if userLoggedIn {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.navigationBarHidden = true
navigationController.setViewControllers([tabBarController], animated: true)
} else {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: ViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as! ViewController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.navigationBarHidden = true
navigationController.setViewControllers([tabBarController], animated: true)
}
If I go with the second option, I can easily transition to the app's main flow (let's say a tab bar controller) after I'm done logging in the user. In an appropriate place in the LogInViewController, I can say:
// Transition to tab bar controller
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let tabBarController: UITabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
self.navigationController?.setViewControllers([tabBarController], animated: true)
Pretty easy.
As for the first method, I am not sure how I would transition to the main content of the app after logging the user in.
What I am looking for here is the "best" way to handle login flow. I've listed two methods but maybe there is another that is even better. Also, if the "best" method is the first I have listed, how can I get to my tab bar controller after I finish logging the user in? Thanks!
Those are both fine options. One thing that I've done that has worked well is implemented a custom container view controller as the root of my app (this might be a subclass of my main view controller like a tab bar controller or navigation controller).
In this container view controller, I can put code to display and dismiss a login view controller based on the user's login status. Note: you can also use temporary full-screen views here to hide your app's main content while the login screen is shown/dismissed - this is an easy way to get very smooth app launch transitions.
I particularly like this method, because the rest of the app doesn't need to worry about the details. Your container can act like a normal tab bar controller or navigation controller, but underneath, you get to encapsulate all of the login UI logic in one place.

Resources