Setting RootViewController in Appdelegate - ios

I have storyboard in my application, but i don't want to set the Main storyboard file base name in info.plist, as well as I don't want to set the entry point i.e Is Initial View Controller in storyboard for any viewcontroller scene.
Though I want to launch the app with some scene from the storyboard, but all shows me a black screen.
When I tried
let testob:testClass = testClass()
self.window?.rootViewController = testob
in AppDelegate, it didn't worked and got the same black screen.
But when I set the Main storyboard file base name in info.plist, and the entry point i.e Is Initial View Controller in storyboard every thing works.
Is there any solution to do so?

You need to instantiate the window and make it key and visible.
let bounds = UIScreen.main.bounds
self.window = UIWindow(frame: bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()

You can load a particular view controller from storyboard.
See this answer.
Example:
window?.rootViewController = initialViewController()
where
private func initialViewController() -> UIViewController {
if isUserLoggedIn {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoadingViewControllerIdentifier")
} else {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("HomeViewControllerIdentifier")
}
}

After setting self.window?.rootViewController = testob, you have to do the following
[self.window makeKeyAndVisible]
Also, initialize the viewController from storyboard using appropriate method

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()

How to set self as new window.rootViewController?

I want to set one of my ViewControllers from my navigation stack as my apps window.rootViewController while I am somewhere in another controller's navigation stack. That other controller is currently my window.rootViewController. Therefore I use this code:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.window?.rootViewController = self
}
This results in just a black screen.
When I instantiate a new ViewController from Storyboard it's working fine, but all entries are gone. I don't want to reconfigure the new viewController, if that is avoidable.
Update A
I put self to a strong reference in appDelegate to check that, because I thought it's view was unloaded somewhere on the way, but it still resulted in a black screen.
The documentation says:
The root view controller provides the content view of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) installs the view controller’s view as the content view of the window. The new content view is configured to track the window size, changing as the window size changes. If the window has an existing view hierarchy, the old views are removed before the new ones are installed.
Clarification: I don't want to instantiate a new VC, I want to use the current one, without putting a navigationController around it, it should not be necessary. I'm sure it is possible without any workaround, I am just missing something here.
To be more precise, you can use navigation controller with your view controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let initialViewController = storyboard.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
let nvc: UINavigationController = UINavigationController(rootViewController: initialViewController)
appDelegate?.window?.rootViewController = nvc
appDelegate?.window?.makeKeyAndVisible()
The documentation says: The root view controller provides the content view of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) installs the view controller’s view as the content view of the window. The new content view is configured to track the window size, changing as the window size changes. If the window has an existing view hierarchy, the old views are removed before the new ones are installed.
Just as the documentation says: It removes all views in the stack if the rootViewController is exchanged. No matter what's with the controller. So I had to remove self from the stack to assure my view won't be removed. This resulted in following solution:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
guard let pageVC = self.onboardingDelegate as? OnboardingPageViewController else { return } // my current stack is in a pageViewController, it also is my delegate
let vc = self // holding myself
pageVC.subViewControllers.removeLast() // removing myself from the list
pageVC.setViewControllers([pageVC.subViewControllers[0]], direction: .forward, animated: false, completion: nil) // remove the current presented VC
appDelegate.window?.rootViewController = vc
vc.onboardingDelegate = nil
appDelegate.window?.makeKeyAndVisible()
}
And it is working fine, just like I wanted.
You have to remove it from the navigation controller before setting it as the root view controller:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
willMove(toParentViewController: nil)
view.removeFromSuperview()
removeFromParentViewController()
appDelegate.window?.rootViewController = self
}
NOTE: this is not the best approach, you should revisit your navigation stack.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
let objLoginViewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
appdelegate.window?.rootViewController = objLoginViewController
appdelegate.window?.makeKeyAndVisible()
}
if you don't want to instantiate new controller then you can find that
controller from your navigation stack and assign into
rootViewController
You are on the right track, but just add your self to NavigationController and then set it as rootViewController and makeKeyAndVisible the window.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
let nav = UINavigationController(rootViewController: self)
// IF YOU DON'T WANT NAVIGATION BAR, set it hidden
nav.navigationBar.isHidden = true
appDelegate.window?.rootViewController = nav
appDelegate.window?.makeKeyAndVisible()
}
Try and share your results!

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.

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

How to dismiss any popover viewcontrollers when activated in 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.

Resources