Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Login Screen with Swift and iOS 8 Storyboard

Tags:

xcode

ios

swift

I have having a lot of trouble handling the login flow of my iOS app. An image of the storyboard I am trying to achieve is below

enter image description here

I am trying to achieve an optional login screen which is presented only when a user first opens the app and has not logged in. Currently, I have the Tab Bar Controller set as the root view controller. I can not figure out how to handle swapping between these view controllers, however.

I have tried to simply push the login screen with the following code. However, it does not work. I believe the problem that a tab bar controller is not allowed to push new view controllers.

    func application(application: UIApplication, didFinishLaunchingWithOptions  launchOptions: [NSObject: AnyObject]?) -> Bool {
    //stuff

    if userLoggedIn {
        // Do nothing
    } else {
        //get access to login view
        var storyboard = UIStoryboard(name: "Main", bundle: nil)
        var viewController =    storyboard.instantiateViewControllerWithIdentifier("signupView") as UIViewController;

        // Then push login view
        var rootViewController = self.window!.rootViewController as UITabBarController;
        rootViewController.pushViewController(viewController, animated: true)
    }

    // Override point for customization after application launch.
    return true
}

Is there a way to switch view controllers without pushing within a navigation controller? Any advice on how to handle this sort of login flow would be greatly appreciated.

like image 904
user3684980 Avatar asked Mar 02 '15 19:03

user3684980


2 Answers

I achieve this with a "LaunchViewController" that determines if it should present the Login view or the main navigation controller.

Mark the launch view your initial view in the storyboard and write the logic in there to determine which to present. This process is often very quick and unnoticeable to the user.

UPDATE:

As mentioned in my comment I've gone a different direction. Now in the App Delegate's application(application:didFinishLaunchingWithOptions:) method I perform the logic necessary to determine which view should be the rootViewController and set it there using the following.

extension AppDelegate {

    enum LaunchViewController {
        case Login, Dashboard

        var viewController: UIViewController {
            switch self {
            case .Login: return StoryboardScene.Login.LoginScene.viewController()
            case .Dashboard: return StoryboardScene.Dashboard.initialViewController()
            }
        }

        /// Sets `UIWindow().rootViewController` to the appropriate view controller, by default this runs without an animation.
        func setAsRootviewController(animated animated: Bool = false) {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let window = appDelegate.window!
            let launchViewController = viewController

            log.info?.message("Setting \(launchViewController.dynamicType) as rootViewController")
            if let rootViewController = window.rootViewController where rootViewController.dynamicType != launchViewController.dynamicType && animated {
                let overlayView = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
                launchViewController.view.addSubview(overlayView)

                UIView.animateWithDuration(0.3, animations: {
                    overlayView.alpha = 0.0
                    },
                    completion: { _ in
                        overlayView.removeFromSuperview()
                });
            }

            window.rootViewController = launchViewController
            window.restorationIdentifier = String(launchViewController.dynamicType)

            if window.keyWindow == false {
                window.makeKeyAndVisible()
            }
        }
    }
}

Note that StoryboardScene is not a default type, I am using https://github.com/AliSoftware/SwiftGen to generate that.

My App Delegate method looks something like this.

if window == nil {
    window = UIWindow(frame: UIScreen.mainScreen().bounds)
}

if isRestoringState == false {
    if let _ = lastUsedAccount {
        LaunchViewController.Dashboard.setAsRootviewController()
    } else {
        LaunchViewController.Login.setAsRootviewController()
    }
}

UPDATE with Logout method for Comment

func handleLogout(notification: NSNotification) {
  LaunchViewController.Login.setAsRootviewController(animated: true)
  lastUsedAccount = nil
}
like image 104
Chris Wagner Avatar answered Nov 07 '22 04:11

Chris Wagner


Here is what i did and it works fine :

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if (!AppData.sharedInstance.loggedIn) {
        let signInNavigation = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("SignInNavigationControllerIdentifier") as? UINavigationController
        self.window!.rootViewController = signInNavigation;
    }
    return true
}
like image 31
karl j Avatar answered Nov 07 '22 04:11

karl j