IOS: Tab Bar Controller as root controller after login - ios

I have a Storyboard and I have added the default UITabViewController, which has two UIViewControllers.
-FirstViewController
-SecondViewController
So the root arrow point to the tab bar controller in the Storyboard.
Now I have added a LoginViewController (that isn't a tab of the Tab Bar).
In the AppDelegate file, in the didFinishLaunchingWithOptions method if the user is yet authenticated, the application is going to show the first tab. Did I code it right?
Please look at the code below:
//inside didFinishLaunchingWithOptions
...
var storyboard = UIStoryboard(name: "Main", bundle: nil);
var tabController = storyboard.instantiateViewControllerWithIdentifier("TabBar") as UITabBarController;
if(self.window != nil){
self.window!.rootViewController = tabController;
}
The Tab Bar Controller doesn't have a custom class associated but the generic one. Did I take the right approach?

This is correct for manually instantiating a storyboard as a root view controller. However, manually setting the rootViewController in didFinishLaunchingWithOptions isn't a great strategy. For one, you have to manually load the storyboard and then instantiateViewControllerWithIdentifier, which is inflexible and can break.
If you want to show a login screen first if the user is not logged in, try:
if (checkUserNotLogedIn()) {
//user not logged in
var loginViewController = CustomLoginViewController()
self.window?.rootViewController?.presentViewController(loginViewController, animated: false, completion: nil)
}
Now your custom login controller is sitting on top of the tabBar.

Related

When change root view controller, initial view controller is displaying for one or half second

In the app FIRSTViewController is initial View Controller set from storyboard. Login, Register, etc option available there. Login screen is presented from it:
let destVC = self.storyboard?.instantiateViewControllerWithIdentifier("LoginIdentifier")
self.presentViewController(destVC!, animated: true, completion: nil)
When user logged in successfully, redirect it to TabViewController:
let desViewController = selfVC.storyboard!.instantiateViewControllerWithIdentifier("UITabBarController") as! UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = desViewController
Above code is working fine, but issue is: When redirection to tab view is happening, initial view controller(FIRSTViewController) is display for one or half second before display tabbar.
So, my question is:
How can i prevent initial view controller from display?
That's may happen because of this code was executed after InitialViewController loaded
let desViewController = selfVC.storyboard!.instantiateViewControllerWithIdentifier("UITabBarController") as! UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = desViewController
To fix that issue you must not set FirstViewController as Initial, you can set some Other "empty" View Controller as Initial and redirect to Login/register or on Tab ViewController, in Initial ViewController you can set ImageView to screen with lunch image, so user will not know that some controller was showen, its will look like lunch screen is steel showing.

Present View Controller Over current tabBarController with NavigationController

When presenting or dismissing VC, I do not want to keep hiding and showing tabBar because it creates a poor user experience. Instead, I want present the next VC straight over the tab bar such that when I dismiss the nextVC by dragging slowly from left to right, I can see the tabBar hidden behind the view (As shown in image below)
Note, my app has two tabs with two VCs(VCA,VCB) associated to it. Both VC also have navigation bar embedded. VCA segues to VCA1 and VCB segues to VCB1. At the moment, inside VCA and VCB I am calling the following function to segue with some hiding and unhiding done when viewWillappear (Code below).
self.navigationController?.showViewController(vc, sender: self)
// Inside ViewWillAppear Only reappear the tab bar if we successfully enter Discover VC (To prevent drag back half way causing tab bar to cause comment entry to be floating). This code check if we have successfully enters DiscoverVC
if let tc = transitionCoordinator() {
if tc.initiallyInteractive() == true {
tc.notifyWhenInteractionEndsUsingBlock({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
if context.isCancelled() {
// do nothing!
}
else {
// not cancelled, do it
self.tabbarController.tabBar.hidden = false
}
})
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
} else {
// not interactive, do it
self.tabbarController.tabBar.hidden = false
}
----------Working solution from GOKUL-----------
Gokul's answer is close to spot on. I have played with his solution and came up with the following improvement to eliminate the need to have a redundant VC and also eliminate the initial VC being shown for a brief second before tabVC appears. But without Gokul, I would never ever come up with this!!
Additionally, Gokul's method would create a bug for me because even though I do have a initial "normal" VC as LoginVC before tabVC is shown. This loginVC is ONLY the rootVC if the user needs to login. So by setting the rootVC to tabVC in most cases, the navVC will never be registered.
The solution is to embed navigation controller and tabBar controller to one VC. But it ONLY works if the navVC is before the TabBarVC. I am not sure why but the only way that allowed me to have navVC-> tabVC-> VC1/VC2 is to embed VC1 with a navVC first than click on VC1 again to embed tabVC (It wouldn't allow me to insert one before tabVC and I also had to click the VC1 again after embedding the NavVC).
For your requirement we need to make some small changes in your given view hierarchy
Let me explain step by step,
To meet your requirement we have to add a UIViewController(let's say InitialVC) embedded with a UINavigationController and make it as initial viewcontroller.
Then add a UITabbarController with 2 VC (VCA,VCB) // IMPORTANT: Without any navigationcontroller embedded.
Add a segue between InitalVC and TabbarController with an unique identifier(ex: Initial)
In viewWillAppear of InitalVC perform segue as below (InitialVC is unnecessary to our design we are using this just to bridge navigationController and tabbarController).
self.performSegueWithIdentifier("Initial", sender: nil)
In TabbarControllerclass hide your back button, this ensures that InitialVC is unreachable.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
}
Now add a segue from a button between VCA and VCA1, thats it build and run you will see VCA1 presenting over VCA's tabbar.
What we have changed?
Instead of adding UINavigationController inside UITabbarController we have done vice versa. We can't directly add Tabbar inside navigation to do that we are using InitialVC between them.
Result:
1st way is create a image of the tabbar using UIGraphicsGetImageFromCurrentImageContext and set it on the bottom of the other view...
2nd way is show the next view in another new window that is above the tabbar, that way you wont need to hide the tabbar anymore, but seems like its in the navigation controller so this way doesnt seems available
Hiding and unhiding the tab bar is unnecessary. You only need to embed the UITabBarController inside the UINavigationController. That is, UINavigationController as the initial vc, UITabBarController as the root vc of UINavigationController.

override screen when move another scree in swift

I am new in swift.I want to move one screen to another but problem is when I go to another screen,old screen overrides in new screen.
here is my code
dispatch_async(dispatch_get_main_queue()) {
let appsDelegate = UIApplication.sharedApplication().delegate
appsDelegate?.window!!.rootViewController = nil
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("launcherEntry") as! UINavigationController
appsDelegate?.window!!.rootViewController = nextViewController
}
If you use UINavigationController, you should push the second view(screen) onto the first one. Check out the offical guide.
According to your code old screen overrides because you have not a push a UIViewController into navigation controller.
Here you destroyee the exiting controller then set a new controller. So that previos controller is move from the memory.
Please show Apple docs for the UINavigationController.
Update:
Please use following code for set a root view controller then you can push view controller easily.
let navigationController:UINavigationController = storyboard.instantiateInitialViewController() as! UINavigationController
let rootViewController:UIViewController = storyboard.instantiateViewControllerWithIdentifier("ID_LoginVC") as! LoginVC
navigationController.viewControllers = [rootViewController]
self.window?.rootViewController = navigationController
As you are new , i will explain what will happen when your code will be executed.
App delegate will hold the current instance of our application. Setting root view controller is setting Main view controller of your application.
SO basically your code will replace the previous view controller with next one.
Now what you have to do,
We have Navigation controller to manage our navigation stack, that allow us to go back from where have initiated.
If you are Using storyboard , add your view controller into NavigationController, if you already know then ignore or follow this
SELECT your first view controller GOTO Editor in Xcode menu > Select EMBED IN option > NAVIGATION CONTROLLER. this will add your first view into navigation controller.
Now when you want to display next view controller, There are two ways to do so,
1)Using segue from Storyboard
Right click from view/button from where you want to show next view.
drag it to the next view controller and release the right click.
Select push
2)Programatically
you don't have to set root view controller, just push the next view controller in navigation controller you have
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("launcherEntry") as! UINavigationController
Above line will intimate the textview controller, now we have to push this view controller, we can achieve this with
self.navigationController?.pushViewController(nextViewController, animated: true)

How to Push a New View Without Storyboards Swift

First of all, I don't want to use storyboards at all. I am able to "present" the targeted view controller, but I can't get it to show with the standard iOS back button at the top. My understanding is I can get this to work by pushing the controller instead of presenting it. I don't get any errors, but nothing happens.
On a button click this code is run. The commented code is what successfully presented the ForgotPasswordPage :
// Changes to Forgot Password Page
func forgotPasswordSwitch(sender: UIButton!) {
//let ForgotPassword:ForgotPasswordPage = ForgotPasswordPage()
//self.presentViewController(ForgotPassword, animated: true, completion: nil)
let ForgotPassword = ForgotPasswordPage()
self.navigationController?.pushViewController(ForgotPassword, animated: true)
}
You have to manually create a UINavigationcontrollerto get the back bar. To do this you can use the code from this answer, which achieves this by using this code:
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var nav1 = UINavigationController()
var mainView = ViewController() //ViewController = Name of your controller
nav1.viewControllers = [mainView]
self.window!.rootViewController = nav1
self.window?.makeKeyAndVisible()
Here just add all the ViewControllers you want to be under the Navigation controller into the array and then push between them.
Hope that helps, Julian
I know that sometimes it's much better develop apps programmatically, but there are many time that Storyboard can save you a lot of time. You just simply use it for this situations...
1) In you Storyboard, localize the view controller you want to enable for pushing
2) Select it and find the "Editor" in your top bar
3) Select Editor->Embed In->Navigation Controller
And now your view controller it's ready for pushing

Where is my Navigation Bar?

Here are two screenshots, my Storyboard:
And my emulator:
I have localized the problem further. I have a login screen which I show if the user is not logged in. the navigation bar is not visible only after login: I suppose it has to do with the way I switch from login screen to the root view controller. Here is the way I do it:
func switchToMainScreen() {
let rootController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("homeView");
self.presentViewController(rootController, animated: true, completion: nil)
}
Can anyone help me as to why there is no Navbar + Navbar item in my emulator?
I can't post comment on your question for my reputation, check "bar visibility" at attributes from your storyBoard.make sure it's checked, maybe it's unchecked.
i can't post image in my answer, see following link for more info:
http://i.stack.imgur.com/1k4WU.png
tell me the result to check another odds.
//UPDATE
I think you need to embed your viewController in NavigationController . I mean:
delete your NavigationController from storyBoard,
second select your viewController
then from top menu bar select "Editor" -> "Embed in" -> "navigationController"
it automatically create an navigation Item which can contain navigation items.
From screenshot it looks ok. The only thing that I worry about is the navigation bar in your view controller with title as "Title".
Could you please try this:
Delete the Navigation Controller from your storyboard.
Delete the custom navigation bar in your view controller.
Select your View Controller in your story board, then choose
"Editor" in your menu, then "Embed in" => "Navigation Controller".
Finally, in your view controller' viewDidLoad:, try the following:
->
self.navigationItem.title = #"My Title";
self.navigationItem.rightBarButtonItem = // Set your right bar button item here
EDIT: Post OP question update:
Since your entry point is not the story board driven and you have an initial login screen, I would advise you to create your own UINavigationController and embedded your storyboard view controller into it and then present it. It must work!!!
func switchToMainScreen() {
let rootController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("homeView")
let navigationController = UINavigationController(rootViewController: rootController)
self.presentViewController(navigationController, animated: true, completion: nil)
}
PS: Remove the embedded Navigation Controller from storyboard then.

Resources