I have a tabbar app with an initial login screen. The tabbarController is set as the initial view in Storyboard with 1 VC that has a navigationController also embed.
I have a loginVC instantiated and set as rootViewController in my AppDelegate. After the user has successfully sign in from the loginVC, I need to switch over to the tabbarController. Would I try to get a reference to the tabbarcontroller and set it as the new rootviewcontroller? If so, I'm having a hard time figuring out how to do this:/
AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if NSUserDefaults.standardUserDefaults().objectForKey("OAuth") == nil {
self.window = UIWindow(frame:UIScreen.mainScreen().bounds)
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var loginVC = storyboard.instantiateViewControllerWithIdentifier("LOGIN_VC") as LoginVC
self.window?.rootViewController = loginVC
self.window?.makeKeyAndVisible()
}
return true
}
This method gets called after user has successfully signed in
func dismissLoginVC() {
var tabbarController = self.storyboard?.instantiateViewControllerWithIdentifier("TABBAR") as UITabBarController
self.presentViewController(tabbarController, animated: true, completion: nil)
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.window?.rootViewController = tabbarController
}
I know the problem with this is it just created a new tabbarcontroller, rather than referencing the existing tabbarController that was set as the initialView in storyboard. This seems to work but it is missing other items from the navigation bar.
Thanks for pointing me in the right direction!
I think you should change your app structure. Keep the tab bar controller as the initial view controller in the storyboard, and present modally (with no animation) the login controller from the viewDidAppear method of the controller in the first tab -- it will be the first thing the user sees. When you dismiss it, you will be back to that controller in the first tab. With this approach, you don't need any code in the app delegate.
Related
When developing an app, if I only want to test one screen, say, the third tab of a tab view, how would I make the app navigate there on start up?
I think a good solution is use a UI test to automatically navigate to the correct place in the app. Pausing on a breakpoint at the end of the test leaves the app to be played with manually.
rootViewController of window property in AppDelegate will be the first view controller shown on screen. you can make it by programming or using storyboard
by programming:
if NavigationController is the rootViewController of your window, put your own viewController in the first place (index at 0) of NavigationController's viewControllers which is an array. it will be on screen by default.
customNavigationController.viewControllers = [yourViewController]
or simply set your viewController to the rootViewController to the window property in appDelegate
AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
// window?.rootViewController = CustomTabBarViewController()
// customViewController will show on screen by default.
window?.rootViewController = CustomViewController()
window?.makeKeyAndVisible()
return true
}
by interface builder:
check your viewControlelr's attributes inspector panel, and check "is Initial View Controller" option, then you can see a simple arrow attached to this view controller.
or:
add identifier to your viewController, and fetch it from storyBoard, then set it to the rootViewController of the window object in appDelegate
let testController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "testController") as! TestController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = testController
I am building an iOS app in swift and upon launching the app I would like to first show viewController2, which is presented on top of viewController1, which is embedded in a UINavigationController. The key part being that ViewController2 is not part of the navigation stack and is presented instead of being pushed.
This is my current attempt which doesn't work and only shows ViewController1 upon launch.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController1 = mainStoryboard.instantiateViewController(withIdentifier: "VC1") as! ViewController1
let viewController2 = mainStoryboard.instantiateViewController(withIdentifier: "VC2") as! ViewController2
let navController = UINavigationController(rootViewController: viewController1)
viewController1.present(viewController2, animated: false)
self.window?.rootViewController = navController
self.window?.makeKeyAndVisible()
return true
}
I have this structure in place because I enable a user to be able to swipe from left to right and right to left to get to viewController2 and viewController3 in a similar fashion to Snapchat. Presenting these controllers seemed the best idea as they don't have navigation bars and once finished you would want to return to viewControlller1 - perhaps I need to change the structure of my app but would ideally not like to as I aim to submit this mvp within the next week.
Help much appreciated from you iOS and swift wizards.
// Think I need to change the structure of the app to include viewController2 on the navigation stack :( as using accepted answer causes viewController1 to be seen briefly before viewController2 is presented which is logical - in the long run this will be better despite the short term pain
rootViewController should be set before everything. Do it in this order and it should work fine.
self.window?.rootViewController = navController
self.window?.makeKeyAndVisible()
navController.present(viewController2, animated: false)
I am developing an application based on UITabbar and the view hierarchy as follows.
UITabBarController ----> UINavigationController ----> UIViewController
I have push notification payload which will open specific UIViewController, i can explicitly open UIViewController directly using view controller Storyboard ID, but tabBar and Navbar won't show.
How can I go to specific View Controller and show TabBar and NavController from AppDelegate didReceiveRemoteNotifications.
Thanks!
You have to instantiate all of your VC and set all of them as root of his predecessor :
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("YourVC_Identifier");
let yourNavController = mainStoryboard.instantiateViewControllerWithIdentifier("YourNAV_Identifier") as! UINavigationController
let yourTabController = mainStoryboard.instantiateViewControllerWithIdentifier("YourTAB_Identifier") as! UITabBarController
yourNavController.setViewControllers([yourVC], animated: false)
yourTabController.setViewControllers([yourNavController], animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = yourTabController
self.window?.makeKeyAndVisible()
return true
}
[[UIApplication sharedApplication] keyWindow] has a property .rootViewController. Presumably thats your tab bar. On this controller, you can set the active tab and switch out view controllers with the .viewControllers property. Now assuming one of these is your UINavigationController that should also have a property .rootViewController. Instantiate from the storyboard and either set the root or push the view controller on top of the navigation controller.
Follow the hierarchy from App Delegate programmatically.
In case your entry point is from the Storyboard, setup a UIWindow in your AppDelegate so you could set the UITabBarController as the following.
//self.tabBarController is you TabBar from Storyboard, or programatically initialized
self.window.rootViewController = self.tabBarController;
Then whenever you have a notification in didReceiveRemoteNotifications sort the notification out by type, and find the view controller:
//Let's say the View Controller being accessed is in the first position of the stack of viewcontroller from UITabBarController & UINavigationController
UINavigationController *navViewController = self.tabBarController.viewControllers.firstObject;
UIViewController *accessedViewController = navViewController.viewcontroller.firstObject;
I am trying to accomplish what I think is a pretty common set of steps:
When my app starts, load a home controller in a navigation controller:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.rootViewController = UINavigationController(rootViewController: HomeViewController())
window.makeKeyAndVisible()
self.window = window
return true
}
When my app loads check if the user is logged in. If not, present a registration view controller:
func applicationDidBecomeActive(application: UIApplication) {
if ref.loggedIn != nil {
// user authenticated
print(ref.userData)
} else {
// No user is signed in
let registerViewController = RegisterViewController()
if let navController = window?.rootViewController as? UINavigationController {
navController.presentViewController(registerViewController, animated: true, completion: nil)
}
}
}
In my RegisterViewController, provide a button that switches to a LoginViewController:
#IBAction func login(sender: UIButton) {
print("LOGIN")
let loginViewController = LoginViewController()
// How do I present the view controller here?
}
My question is: how can I present the LoginViewController so that calling self.dismissViewControllerAnimated(false, completion: nil) will return to my HomeViewController?
Things I've tried:
If I call self.presentViewController from the RegisterViewController then dismissing returns back to the RegisterViewController instead of HomeViewController
If I try to get a reference to rootViewController via UIApplication.sharedApplication() and present the login controller on rootViewController then I get an error "Attempt to present ... on ... whose view is not in the window hierarchy!"
Thanks!
It's obvious that you will get the error "Attempt to present ... on ... whose view is not in the window hierarchy!"
First you need to make the instance of ViewController using storyboard identifier.
Please try this :-
let storyboard = UIStoryboard(name: "YourStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("viewControllerToBePresented") as! UIViewController //use your class name here to cast it.
self.presentViewController(vc, animated: true, completion: nil)
EDIT
In your case you can use protocol on register screen which will get
implemented on home screen.
And in that implementation you can write code to dismiss register view
and then present Login view.
If you are trying to present a new view controller add that view controller in navigation controller and then present the navigation controller..
From the presented view controller you can dismiss to previous view controller.
I hope this will work..
In your case you can use protocol on register screen which will get implemented on home screen.
And in that implementation you can write code to dismiss register view and then present Login view.
My application has a ViewController i.e. the first screen that opens when app is started. I added a UITabBarController to the application through mainstoryboard. I changed the title of UITabBarController to "myTabBarController". Now how do I access this TabBarController from the viewDidLoad function of already existing ViewController?
amazed! Why do you need view controller if you want to load UITabBarController from viewDidLoad function of view controller???
However solution can be like this:
You can make segue: right click in view controller then drag to tabBarController and select segue "show".
But, better would be if you embed UITabBarController to view controller which loads your TabBarController at the beginning of your app.
1: Select View Controller
2: Go to [Editor] in menu bar of xcode
3: Select Embed In
4: Select Tab Bar Controller
and you are done :)
other solution would be:
delete the ViewController from storyboard
select tabBarController that you added.
go to "attribute inspector" in left pane and select "Is initial View Controller"
ADDED
1. Give identifier to TabBarController like given by 張家齊
2. Add button in ViewController and make Action of it in ViewController's class (to make action. right click the added button and drag to ViewController class and select Action instead of Outlet.)
3. Now add following codes in that action:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewControllerWithIdentifier("myTabBarController") //vc is instance of TabBarController.
self.presentViewController(vc, animated: true, completion: nil)
DONE :)
In AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let main = storyboard.instantiateViewControllerWithIdentifier("myTabBarController") as! myTabBarController
self.window!.rootViewController = main
self.window!.makeKeyAndVisible()
return true
}
And you should set Identifier of myTabBarController in your Main.storyboard.