iOS Login Screen Modal on top of Tab Bar or Instantiated - ios

I am developing an iOS App that has several Tabs. So I decided my root view Controller will be a Tab Bar Controller with 2 views.
I need the user to login as soon as the application starts, so I'm trying to find out the best approach to do this.
Following other similar questions posted here I've tried this 2 approaches:
Presenting the login screen as a Modal View on top of the Tab Bar Controller
Instantiating the Login view controller
I've done this, by using a UserisLoggedIn flag in NSUserDefaults that is set every time the user logs in or logs out.
The problem I face with both approaches is that before the login screen appears there's a quick flickr of the first view in the Tab Bar.
I've tried to resolve this setting the alpha value of the Tab Bar to 0 if the user is not logged in for the modal approach, but for the instance approach it doesnt work, it still flickers before showing the login screen. Anyway, I find this solution tedious and not really elegant.
There must be a standard approach for doing this in iOS, since you have loads of apps that present first a login screen, but since I am a beginner with iOS Programming I still don't know how to do it.
Here's some code from my Tab bar controller:
1st Approach
Tab Bar Controller
override func viewDidLoad() {
super.viewDidLoad()
view.alpha = 0
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") as NSString == "loggedIn"{
view.alpha = 1.0
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(false)
self.showLoginView()
}
func showLoginView(){
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
self.performSegueWithIdentifier("loginView", sender: self)
}
}
Login View Controller
func updateUserLoggedInFlag() {
// Update the NSUserDefaults flag
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("loggedIn", forKey: "userLoggedIn")
defaults.synchronize()
}
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
self.performSegueWithIdentifier("dismissLogin", sender: self)
}
2nd Approach, Creating an Instance of the Login View Controller
1st View in the Tab Bar
override func viewDidAppear(animated: Bool){
super.viewDidAppear(true)
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
let loginController: LoginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.tabBarController?.presentViewController(loginController, animated: false, completion: nil)
}
Login View Controller
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
self.dismissViewControllerAnimated(true, completion: nil)
}

OK here is what did the trick for me. I don't know if it's the right thing to do, but it seems to work. Like Joseph Duffy mentioned all I did was to set the Login view as the rootviewcontroller in the delegate, and once I was succesfully logged in, switch back to the tab bar controller as rootviewcontroller.
Code in AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
showLogin()
}
}
func showLogin() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = loginViewController
}
Code in LoginViewController
#IBAction func login(sender: AnyObject) {
//Do my login here...
//If login successful:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
self.dismissViewControllerAnimated(true, completion: nil)
appDelegate.window?.rootViewController = storyboard.instantiateViewControllerWithIdentifier("tabBarID") as TabBarController
}

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("userLoggedIn") == nil{
showLoginView()
}
func showLoginView(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController: LoginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController?.presentViewController(loginViewController, animated: true, completion: nil)
}

Take a look at a similar question's answer. The solution is to do this check in your app delegate's application:didFinishLaunchingWithOptions: method by checking if the user is logged in and loading the login view from a storyboard when necessary.
I have created a blank project, added a view controller with a storyboard identifier of loginScreen and have the following code in the app delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.showLogin()
return true
}
func showLogin() {
//loginScreen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewControllerWithIdentifier("loginScreen") as UIViewController
if let window = self.window {
window.makeKeyAndVisible()
window.rootViewController?.presentViewController(loginViewController, animated: false, completion: nil)
}
}
This shows the login screen and the main screen is not seen.

Related

How do I dismiss a rootViewController before presenting a new UITabBarController?

I've set up a UIViewController as my rootViewController in AppDelegate, however, when a user logs in or skips it I am presenting a UITabBarController over the top.
I need to dismiss the LoginController, and set the UITabController as rootViewController instead after user logs in.
How can I go about reorganizing this?
AppDelegate()
window = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = LoginController()
LoginController()
self.present(MainTabBarController(), animated: true, completion: nil)
you can design your code like this.
This one
It's too easy to maintain condition for user logged in or not, and then based on that status. You can navigate to your view.
You can try this way :
1.In AppDelegate you can these methods.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let isLogin = UserDefaults.standard.bool(forKey: "IS_LOGIN")
if isLogin == true {
self.goToDashboardView()
} else {
self.goToLoginView()
}
return true
}
//MARK:- ------- Global Methods -------
class func sharedInstance() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
func goToLoginView() {
let sb = UIStoryboard.init(name: "Main", bundle: nil)
let loginVC = sb.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let navVC = UINavigationController(rootViewController: loginVC) // You can skip this if you do not want to add navigation bar
self.window?.rootViewController = navVC // if you skipped above line, then you have to assign 'loginVC' here.
self.window?.makeKeyAndVisible()
}
func goToDashboardView() {
let sb = UIStoryboard.init(name: "Main", bundle: nil)
let tabbarVC = sb.instantiateViewController(withIdentifier: "MyTabBarController") as! MyTabBarController
self).window?.rootViewController = tabbarVC // if you skipped above line, then you have to assign 'loginVC' here.
self.window?.makeKeyAndVisible()
}
2.In LoginViewController, when user logged in successfully.
#IBAction func btnLoginClicked(_ sender: UIButton) {
// Your API call or other code
// If all things goes well, then login and go to dashboard
UserDefaults.standard.set(true, forKey: "IS_LOGIN")
AppDelegate.sharedInstance().goToDashboardView()
}
3.And finally, whenever and from wherever you want to log out from app, just call below code.
#IBAction func btnLogOutClicked(_ sender: UIButton) {
// Your API call or other code
// If all things goes well, then logout and go to login view
UserDefaults.standard.set(false, forKey: "IS_LOGIN")
AppDelegate.sharedInstance().goToLoginView()
}
4.And Main.storyboard should have design something like :
you do not need to present the UITabBarViewController and then dismiss the LoginViewController, you just need to reset the UITabBarViewController as a rootViewController for you app after login or skip as the following:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
// Override point for customization after application launch.
let yourTabBar = UIStoryboard(name: "YOUR_STORYBOARD_NAME", bundle: nil).instantiateViewController(withIdentifier: "YOUR_UITABBARCONTROLLER_ID")
self.window!.rootViewController = yourTabBar
self.window!.makeKeyAndVisible()
return true
}

How to maintain login session in ios?

I would like to know where I need to change the root view controller in the app programming. Below is my code
class NavigationViewController: UINavigationController {
var window: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let newRootView = storyBoard.instantiateViewController(withIdentifier: "WelcomeViewController") as! WelcomeViewController
let userStatus = UserDefaults.standard.bool(forKey: "isLoggedIn")
if userStatus {
self.window?.rootViewController = newRootView
}
}
}
I am setting the value for isLoggedIn key as true when user logged in and false when the user logged out so that I can retrieve the value in this navigation controller subclass, based on that I can choose the root view controller. But its not working fine it remains the same whenever I am launching the app I am getting the login page only though I already logged in, also please don't suggest me to change the root view controller inside of didfinishlaunchingwithoptions method. Could someone help me to solve it? Thanks in advance.
If I understand correctly the goal of your code, you want to boot directly on WelcomeViewController if the user is already logged in.
You could try to create a custom segue called ReplaceSegue and set this ReplaceSegue to the segue linking the NavigationViewController and the ViewController.
And then you can change the destination of your segue.
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let newRootView = storyBoard.instantiateViewController(withIdentifier: "WelcomeViewController") as! WelcomeViewController
sourceViewController.navigationController?.setViewControllers([newRootView], animated: true)
}
}
First set user login bool true in NSUserDefults. when User is login Successfully.
UserDefaults.standard.set(true, forKey: "USERISLOGIN")
Now Open Appdelegate.swift -> didFinishLaunchingWithOptions Method:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if(UserDefaults.standard.bool(forKey: "USERISLOGIN")){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let objLogoutVC = storyboard.instantiateViewController(withIdentifier: "LogoutVC") as! LogoutVC
let navigationController = UINavigationController(rootViewController: objLogoutVC)
navigationController.navigationBar.isTranslucent = false
navigationController.navigationBar.isHidden = true
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
return true
}
Let me know if you needed any thing more.

Dismissing initial ViewController

I am developing an app using swift 3 . I launch a tutorial like view controller on the first launch of my app and close it using a button .But the inital view controller does not get dismissed even when i click the close button. I know the question sounds familiar but i am unable to get a correct solution for it .
Here's the code of the the AppDelegate
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application:
UIApplication,didFinishLaunchingWithOptions launchOptions: [
UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if launchedBefore {
print("This is not the first Launch")
} else
{
print("First Launch, setting UserDefaults")
UserDefaults.standard.set(true, forKey: "launchedBefore")
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let initiaView = storyBoard.instantiateViewController(withIdentifier: "FirstLaunchViewController")
self.window?.rootViewController = initiaView
self.window?.makeKeyAndVisible()
}
return true
}
Here's my code from the initialViewController
class FirstLaunchViewController:UIViewController
{
override func viewDidLoad() {
}
#IBAction func doneButtonTapped(_ sender: Any)
{
_ = navigationController?.popViewController(animated:true)
}
}
Try this
let rootViewController = self.window?.rootViewController // this is your initial viewcontroller set initial viewcontroller in storyboard
let initiaView = storyBoard.instantiateViewController(withIdentifier: "FirstLaunchViewController")
rootViewController.pushViewController(initiaView, animated: true)
Try setting the view controller as root view controller for window which you want to show after dismissing the initial view controller. Present the initial view controller from window root view controller

IOS Swift- Navigate to Home screen without Storyboard

I use firebase for authentication. Once user logs in, i want move to home screen with Tab controller.
App Delegate Function
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.makeKeyAndVisible()
navToSignin()
return true
}
func navToHome()
{
print("navToHome")
let customTab = CustomTabBar()
window?.rootViewController = customTab
}
func navToSignin()
{
let firstPage = SigninNewVC()
window?.rootViewController = firstPage
}
SigninNewVC
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
if FIRAuth.auth()?.currentUser?.uid != nil
{
if let appDel = UIApplication.sharedApplication().delegate as? AppDelegate
{
print("viewWillAppear")
appDel.navToHome()
//This func gets called but still does not navigate to Home VC
}
}
}
private func fbSignin(token: String)
{
FIRAuth.auth()?.signInWithCustomToken(token) { (user, error) in
if error == nil
{
if user != nil
{
if let appDel = UIApplication.sharedApplication().delegate as? AppDelegate
{
JulehHUD.hideProgressHUD(self.view!)
appDel.jumpToHome()
//This part works fine when i login it navigates to Home Screen
}
}
}
else
{
print("Error:\n\(error)\n")
}
}
}
When i login for the first time it works fine I am able to Navigate to Home screen. But if i relaunch the App i not able to navigate to the home screen. It stays in the SigninNewVC though the print Statements print("viewWillAppear")& print("navToHome") are executed. Not sure what i am doing wrong. Below is my Tab bar class
class CustomTabBar: UITabBarController
{
override func viewDidLoad()
{
super.viewDidLoad()
let homeController = HomeScreenVC()
let newHomeTab = UINavigationController(rootViewController: homeController)
newHomeTab.title = "Home"
viewControllers = [newHomeTab]
}
}
Kindly let me know what i am doing wrong.
I think you insantiated the VC wrongly, you should be using something like CustomTabBar(nibName: nil, bundle:nil) and add your subview in it's loadView:
Adding below code to navToHome() & navToSignin() in App Delegate fixed the issue.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.makeKeyAndVisible()

Choose which view to load when app runs in Swift

I would like to know which is the best way to do this and how to implement it:
When the app runs, it checks if there's a value in user defaults
NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil If the value is nil, I would like the app to redirect to a view called LoginViewController, and if not nil, go to MainViewController. Both clases are UIViewController.
I was thinking in create a new RedirectViewController as initial View Controller, and check it there, but it doesn't work, it doesn't redirects:
class RedirectOnStart: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil {
self.performSegueWithIdentifier("loginRegisterSegue", sender: nil)
} else {
self.performSegueWithIdentifier("mainViewSegue", sender: nil)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "loginRegisterSegue" {
let nav = segue.destinationViewController as! LoginRegisterViewController
}
if segue.identifier == "mainViewSegue" {
let nav = segue.destinationViewController as! ViewController
}
}
}
If your scenario starts with "when the app runs" you should consider working in the AppDelegate's didFinishLaunchingWithOptions directly.
For example:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow()
if NSUserDefaults.standardUserDefaults().valueForKey("uid") == nil {
self.window?.rootViewController = LoginController()
} else {
self.window?.rootViewController = MainController()
}
self.window?.makeKeyAndVisible()
return true
}
Found that the above answer is written for Objective-C not Swift; currently written it won't load up the actual view controller you want. Below code will work within 'didFinishLaunchingWithOptions".
make sure you have a userdefault (in my case "Username") for the statement to check for. If it find it's empty; that'll direct you to "SignUpVC", if it's not empty, it'll direct you to "LoginVC".
you'll need to ensure you have set your identifiers for your View Controllers on your storyboard (in my case, 'Main').
after this, make sure you have some sort of method setup to go from Login/Signup to the next set of screens, such as a segue.
Hope this helps someone out there, Have a great day.
Swift 3:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.window = UIWindow()
if UserDefaults.standard.value(forKey: "Username") == nil {
let signupViewCon = storyboard.instantiateViewController(withIdentifier: "SignUpVC")
self.window?.rootViewController = signupViewCon
} else {
let loginViewCon = storyboard.instantiateViewController(withIdentifier: "LoginVC")
self.window?.rootViewController = loginViewCon
}
self.window?.makeKeyAndVisible()
return true
}

Resources