简体   繁体   中英

Login Screen with Swift and iOS 8 Storyboard

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

在此输入图像描述

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.

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
}

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
}

Set Is Initial View Controller for LoginViewController.

在此输入图像描述

Remove from AppDelegate.swift and place it in LoginViewController .

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)
    }

Run your project.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM