我正在使用Storyboard构建一个iOS应用程序。根视图控制器是一个标签栏控制器。我正在创建登录/注销过程,它基本上工作正常,但我有一些问题。我需要知道最好的方法来设置这一切。

我想做到以下几点:

在应用程序第一次启动时显示登录屏幕。当他们登录时,转到标签栏控制器的第一个标签。 任何时候他们启动应用程序之后,检查他们是否登录,并直接跳到根标签栏控制器的第一个标签。 当他们手动单击登出按钮时,显示登录屏幕,并清除视图控制器中的所有数据。

到目前为止,我所做的是将根视图控制器设置为标签栏控制器,并创建了一个自定义segue到Login视图控制器。在我的标签栏控制器类中,我检查他们是否在viewDidAppear方法中登录,并执行segue: [self performSegueWithIdentifier:@"pushLogin" sender:self];

我还设置了一个通知,当注销操作需要执行:[[NSNotificationCenter defaultCenter] addObserver:自我选择器:@选择器(logoutAccount)名称:@“logoutAccount”对象:nil];

注销后,我从Keychain中清除凭据,运行[self setSelectedIndex:0],并执行segue再次显示登录视图控制器。

这一切都很好,但我想知道:这个逻辑应该在AppDelegate中吗?我还有两个问题:

他们第一次启动应用程序时,标签栏控制器在segue执行之前简要显示。我已经尝试移动代码到viewWillAppear,但segue不会工作那么早。 注销时,所有数据仍在所有视图控制器中。如果他们登录到一个新帐户,旧帐户数据仍然显示,直到他们刷新。我需要一种方法来清除这很容易登出。

我愿意重新修改。我考虑过让登录屏幕成为根视图控制器,或者在AppDelegate中创建一个导航控制器来处理所有事情…我只是不确定目前最好的方法是什么。


当前回答

更新Xcode 11的@iAleksandr answer,因为Scene kit会导致问题。

取代

let appDelegate = UIApplication.shared.delegate as!应用委托 appDelegate.window?.rootViewController = rootViewController

With

guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,let sceneDelegate = windowScene.delegate as? SceneDelegate         else {
   return       
}
sceneDelegate.window?.rootViewController = rootViewController

打电话给换衣者。updateRootViewcontroller在场景委托而不是应用委托,像这样: func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene. connectionOptions) { Switcher.updateRootViewController () 警卫让_ =(场景为?UIWindowScene) else {return} }

其他回答

不建议在应用委托中这样做。AppDelegate管理与启动、挂起、终止等相关的应用生命周期。我建议从viewDidAppear中的初始视图控制器开始。你可以自我。presentViewController和self。从登录视图控制器中去遣散viewcontroller。在NSUserDefaults中存储一个bool键,以查看它是否第一次启动。

以下是我的霉霉解决方案,以飨未来的围观群众。

1)创建一个协议来处理登录和注销功能:

protocol LoginFlowHandler {
    func handleLogin(withWindow window: UIWindow?)
    func handleLogout(withWindow window: UIWindow?)
}

2)扩展上述协议,并提供此处注销的功能:

extension LoginFlowHandler {

    func handleLogin(withWindow window: UIWindow?) {

        if let _ = AppState.shared.currentUserId {
            //User has logged in before, cache and continue
            self.showMainApp(withWindow: window)
        } else {
            //No user information, show login flow
            self.showLogin(withWindow: window)
        }
    }

    func handleLogout(withWindow window: UIWindow?) {

        AppState.shared.signOut()

        showLogin(withWindow: window)
    }

    func showLogin(withWindow window: UIWindow?) {
        window?.subviews.forEach { $0.removeFromSuperview() }
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.login.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

    func showMainApp(withWindow window: UIWindow?) {
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.mainTabBar.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

}

3)然后我可以使我的AppDelegate符合LoginFlowHandler协议,并在启动时调用handleLogin:

class AppDelegate: UIResponder, UIApplicationDelegate, LoginFlowHandler {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow.init(frame: UIScreen.main.bounds)

        initialiseServices()

        handleLogin(withWindow: window)

        return true
    }

}

从这里,我的协议扩展将处理逻辑或确定用户是否登录/退出,然后相应地更改windows rootViewController !

我和你的情况一样,我找到的清理数据的解决方案是删除所有的CoreData东西,我的视图控制器依赖于绘制它的信息。但是我仍然发现这种方法是非常糟糕的,我认为一个更优雅的方法可以做到这一点,没有故事板,只使用代码来管理视图控制器之间的转换。

我在Github上找到了这个项目,它只通过代码来做所有这些事情,而且很容易理解。他们使用一个类似facebook的侧菜单,他们所做的是根据用户是否登录而改变中心视图控制器。当用户注销时,appDelegate将从CoreData中删除数据,并将主视图控制器再次设置为登录屏幕。

更新Xcode 11的@iAleksandr answer,因为Scene kit会导致问题。

取代

let appDelegate = UIApplication.shared.delegate as!应用委托 appDelegate.window?.rootViewController = rootViewController

With

guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,let sceneDelegate = windowScene.delegate as? SceneDelegate         else {
   return       
}
sceneDelegate.window?.rootViewController = rootViewController

打电话给换衣者。updateRootViewcontroller在场景委托而不是应用委托,像这样: func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene. connectionOptions) { Switcher.updateRootViewController () 警卫让_ =(场景为?UIWindowScene) else {return} }

在Xcode 7中,你可以有多个故事板。如果您能将Login流保存在一个单独的故事板中会更好。

这可以使用SELECT VIEWCONTROLLER > Editor > Refactor to Storyboard完成

这里是设置一个视图为rootviewcontroller -的Swift版本

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    appDelegate.window!.rootViewController = newRootViewController

    let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController")