Swift - UITabBarContorller Initial View - when to force launch login screen? - swift

I have programatically set up a custom UITabBarControler with three tabs, each of which is a UIViewController embedded a UINavigationController.
I am not using storyboards. I set the custom tab controller as the root in AppDelegate
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = CustomTabBarController()
The app runs fine and I get the three tabs and can move between them.
sample of how tabs are populated (in viewDidLoad of the customer tab bar controller)
let ordersVC = OrdersViewController() // where Orders is a UIViewController
ordersVC.title = "Orders"
ordersVC.view.backgroundColor = UIColor.white
let ordersVCNavi = UINavigationController(rootViewController: ordersVC)
ordersVCNavi.navigationBar.tintColor = UIColor.black
...
viewControllers = [homeVCNavi, inventoryVCNavi, ordersVCNavi]
Now I need to first to see if the user is logged in (using Firebase). I can easily check for already logged in (Firebase cached) or not logged in.
I do this logged in check in AppDelegate
My problem is when I need to force a login (jump to login view controller). I can not find a place that works.
- tried placing the call in the custom UITabBarController didLoad and the code is ignored
- tried placing the call in the didLoad and the willAppear in the initial tab controller also ignored
I can place a button on the initial tab and that button will indeed launch the login controller. So I can get to the login controller from a button press.
upon pressing a button I can execute this code and the login controller will show
let vc = LoginViewController()
self.navigationController?.pushViewController(vc, animated: false)
But if I know I need to force login and I try to do that same code snip above in viewDidLoad() or viewWillAppear() in the initial tab controller, or in the custom UITabBarController then the push is ignored. Nothing happens.
What is best practice for forcing login screen when initial view is tabbarcontroller?
Where should one put the navigation to the login controller to force login when not already logged in. Want to go to login so that user can not use the app if not logged in.

in didFinishLaunchingWithOptions
if loggedIn {
window?.rootViewController = CustomTabBarController()
}
else {
window?.rootViewController = LoginViewController()
}
after your login is successful
UIApplication.shared.keyWindow?.rootViewController = CustomTabBarController()

Related

Present Pre-built FirebaseUI Authentication page

I am using the Pre-built Firebase UI authentication for my swift project. The goal is to present the authentication page when the user clicks on the "user profile" button. Otherwise, the user doesn't have to register or sign in.
I have initialized a navigationController. So my initial thought was to simply push the Auth controller to my navigationController.
self.navigationController?.pushViewController(authUI.authViewController(), animated: true)
It failed with the error that no nested navigation controller is allowed as the authViewController() will return an instance of the initial navigation view controller of AuthUI.
My second thought was to simply call Window and set the rootViewController as the authViewController. Then just use the authViewController as the new navigation controller
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = authUI.authViewController()()
Unfortunately, it failed. Nothing showed up but a black screen.
The only way I figured is to call
self.present(authUI.authViewController(), animated: true, completion: nil)
However, the screen will be shown separately, which is not really what I want (see below)
Any thoughts/ideas/ suggestions are greatly appreciated.
I hope this can help you.
let authVC = authUI.authViewController()
authVC.modalPresentationStyle = .fullScreen
self.present(authVC, animated: true, completion: nil)
Thanks.

Back Button Takes Me To Wrong View Controller

I have a storyboard I'm working with that is setup as so:
Login Screen -> Tab Bar Controller - > Navigation Controller - > Screen 1 Segue to Screen 2 Segue to Screen 3.
The first time I login, everything works great. Screen 1 segues to screen 2, screen 2 segues to screen 3, you can then use the back button to go back to screen 2 and then screen 1. However, I have a "logout" function (code below, although I don't think this is relevant to my issue) and after I "logout", it takes me to the Login Screen (first screen in the sequence above). When I then login again, navigate to Screen 2 or Screen 3, pressing the back button from the segue takes me all the way back to the login screen as opposed to the prior Screen 1 or Screen 2.
#objc func logOut(){
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(homeView, animated: true)
homeView.navigationItem.hidesBackButton = true
}
I'm not sure that instantiating the LoginViewController again is the best practice, since you already have it in you navigation stack. I would recommend doing something like this:
#objc func logOut(){
self.navigationController?.popToRootViewControllerAnimated(true)
}
This will remove all the view controllers from the navigtion stack, and present you with a root view controller (LoginViewController)
Updated for anyone who is curious. The pushViewController option was the issue. If i just use "present", I no longer have this issue. Here is my updated logout code:
#objc func logOut(){
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.present(homeView, animated: true)
homeView.navigationItem.hidesBackButton = true
}

clearing variables of root view controller when switching tabs programmatically

I have a storyboard/root viewcontroller as the third tab on my app. On that tab the user selects an image, and writes a caption, which are stored as variables. From the root viewcontroller the user is taken to a preview screen via a segue which passes the variables, i.e. image and text. From this screen the user posts the object. From the post button, I am popping the viewcontroller, and programatically navigating to the first tab of my app. That all works, but the problem is when I then navigate back to the third tab, the stored variables are still there, and I'd like them to be cleared out.
I've seen many posts on keeping the variables, but none on how to reset them. I've tried to use viewWillDissapear but I don't want them cleared when I use the segue because I want the user to be able to go back and make some changes if needed.
UPDATED CODE WITH CORRECT ANSWER
#IBAction func postButtonPressed(_ sender: Any) {
PostFunction.createPost(image: self.postImage, postText: self.hashtag) { (true) in
self.tabBarController?.selectedIndex = 0
let tab3 = self.tabBarController!.viewControllers![2] as! UINavigationController
let vc = tab3.viewControllers.first as! PostHomeVC
vc.clear()
self.navigationController?.popViewController(animated: false)
}
}
Any suggestions on how to "reset" the rootviewcontroller from the above method?
Thanks!
I am popping the viewcontroller, and programatically navigating to the first tab of my app
before you do the programmatic switch to the first tab do
let tab3 = self.tabBarController.viewControllers[2] as! UINavigationController
let vc = tab3.viewControllers.first as! VCName
vc.clear()
or
let vc = self.navigationController!.viewControllers.first as! VCName
vc.clear()
and write that clear method inside the vc as you need

Present viewcontroller with tabbar

I created UITabBarController programmatically in AppDelegate with 4 view controllers(using .xib). When user tap some button on ViewController (VC-A) it present another VC (VC-B) and covered tabbar. So I want to VC-B has a tabbar on the button.
I tried to add VC-B as a child of tabbarcontroller. I tried to .present(vc) and .show(vc) on both: VC-A and VC-A.TabBarController
Creating controllers in AppDelegate:
let controllers = [tabViewController1,tabViewController2,tabViewController3,tabViewController4]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController
presenting in VC-A
self.tabBarController?.present(controller, animated: false, completion: nil)
right click and drag from tabbar controller in storyboard to VC-B. that should create a tab on the bottom of your VC-A and VC-B to go back and forth without having to implement any backend code unless you want to animate
The solution is to embed every VC in navigationController and then add to TabBarController.
let vc1 = ViewController1()
let navController1 = UINavigationController(rootViewController: vc1)
navController.isNavigationBarHidden = true
let controllers = [navController1, navController2, navController3, navController4]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController
Then call
self.navigationController?.pushViewController(controller, animated:
true)
To diplay VC with tabbar
I will press the red login button at the bottom of the picture and try to log in.
login button => "로그인"
After that, log in.
let moreVC = self.storyboard?.instantiateViewController(withIdentifier: "MoreViewController") as! MoreViewController
moreVC.definesPresentationContext = true
moreVC.modalPresentationStyle = .fullScreen
let navController = UINavigationController(rootViewController: moreVC)
self.present(navController, animated: true, completion: nil)
If the login proceeds without error, the above code will be called to display the screen when the login is completed.
If the flow proceeds as the code above, this screen appears.
The screen shown is not fullscreen, and the tabbar at the bottom is gone. The screen I want is the screen below.
How can I present a tab bar when presenting the screen?

How to present unique UITabBarControllers in Swift

I'm working on a project that has two different UITabBarControllers to represent two different states of the app. I can set the first UITabBarController once a user logs in and present the second when a button is pressed. However, I'm getting odd behavior when navigating within the second UITabBarController.
This is how I set the main tab bar.
let mainTabBar = MainTabBarController()
let mainMode = UINavigationController(rootViewController: mainTabBar)
UIApplication.shared.keyWindow?.rootViewController = mainMode
I use an identical method to navigate to the second tab bar.
let secondaryTabBar = SecondaryTabBarController()
let hiddenMode = UINavigationController(rootViewController: secondaryTabBar)
UIApplication.shared.keyWindow?.rootViewController = hiddenMode
However, when using the secondary UITabBarController, I see views from the main UITabBarController when navigating to an AVCaptureSession. More specifically, I see the last view (from which the secondaryTabBar is set) from the mainTabBar under the modal presentation of the capture session. Here's the problem point:
let captureSession = CameraViewController()
navigationController?.present(captureSession, animated: true, completion: nil)
I changed the modalPresentationStyle of the CameraViewController to .overCurrentContext and that solved the issue. Got it from here: Transparent background for modally presented viewcontroller