我正在使用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}
}
以下是我的霉霉解决方案,以飨未来的围观群众。
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 !
更新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")