我正在使用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中创建一个导航控制器来处理所有事情…我只是不确定目前最好的方法是什么。


当前回答

在你的appDelegate中。我在你的didFinishLaunchingWithOptions里面

//authenticatedUser: check from NSUserDefaults User credential if its present then set your navigation flow accordingly

if (authenticatedUser) 
{
    self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];        
}
else
{
    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];
    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];

    self.window.rootViewController = navigation;
}

在SignUpViewController。m文件

- (IBAction)actionSignup:(id)sender
{
    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}

在MyTabThreeViewController.m文件中

- (IBAction)actionLogout:(id)sender {

    // Delete User credential from NSUserDefaults and other data related to user

    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];

    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
    appDelegateTemp.window.rootViewController = navigation;

}

Swift 4版本

didFinishLaunchingWithOptions在app delegate中假设你的初始视图控制器是TabbarController中的signed。

if Auth.auth().currentUser == nil {
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        self.window?.rootViewController = rootController
    }

    return true

在注册视图控制器中:

@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}

MyTabThreeViewController

 //Remove user credentials
guard let appDel = UIApplication.shared.delegate as? AppDelegate else { return }
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        appDel.window?.rootViewController = rootController

其他回答

更新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")

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

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

我不喜欢bhavya的答案,因为在视图控制器内使用AppDelegate和设置rootViewController没有动画。Trevor的回答是关于iOS8上闪烁视图控制器的问题。

UPD 07/18/2015

视图控制器内部的AppDelegate:

在视图控制器内更改AppDelegate状态(属性)会破坏封装。

每个iOS项目中非常简单的对象层次结构:

AppDelegate(拥有窗口和rootViewController)

ViewController(拥有视图)

顶部的对象可以改变底部的对象,因为它们正在创建这些对象。但是如果底部的对象改变它们上面的对象是不行的(我描述了一些基本的编程/OOP原则:DIP(依赖倒置原则:高级模块不能依赖于低级模块,但它们应该依赖于抽象))。

如果任何对象将改变这个层次结构中的任何对象,那么代码中迟早会出现混乱。在小项目上可能没问题,但在小项目上挖掘这个混乱是没有乐趣的=]

UPD 07/18/2015

我复制模态控制器动画使用UINavigationController (tl;dr:检查项目)。

我使用UINavigationController在我的应用程序中呈现所有控制器。最初,我在导航堆栈中显示登录视图控制器与普通的推送/弹出动画。然后我决定用最小的变化把它改成模态。

工作原理:

Initial view controller (or self.window.rootViewController) is UINavigationController with ProgressViewController as a rootViewController. I'm showing ProgressViewController because DataModel can take some time to initialize because it inits core data stack like in this article (I really like this approach). AppDelegate is responsible for getting login status updates. DataModel handles user login/logout and AppDelegate is observing it's userLoggedIn property via KVO. Arguably not the best method to do this but it works for me. (Why KVO is bad, you can check in this or this article (Why Not Use Notifications? part). ModalDismissAnimator and ModalPresentAnimator are used to customize default push animation.

动画师的逻辑工作原理:

AppDelegate sets itself as a delegate of self.window.rootViewController (which is UINavigationController). AppDelegate returns one of animators in -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:] if necessary. Animators implement -transitionDuration: and -animateTransition: methods. -[ModalPresentAnimator animateTransition:]: - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; [[transitionContext containerView] addSubview:toViewController.view]; CGRect frame = toViewController.view.frame; CGRect toFrame = frame; frame.origin.y = CGRectGetHeight(frame); toViewController.view.frame = frame; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^ { toViewController.view.frame = toFrame; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; }

测试项目在这里。

在你的appDelegate中。我在你的didFinishLaunchingWithOptions里面

//authenticatedUser: check from NSUserDefaults User credential if its present then set your navigation flow accordingly

if (authenticatedUser) 
{
    self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];        
}
else
{
    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];
    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];

    self.window.rootViewController = navigation;
}

在SignUpViewController。m文件

- (IBAction)actionSignup:(id)sender
{
    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}

在MyTabThreeViewController.m文件中

- (IBAction)actionLogout:(id)sender {

    // Delete User credential from NSUserDefaults and other data related to user

    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];

    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
    appDelegateTemp.window.rootViewController = navigation;

}

Swift 4版本

didFinishLaunchingWithOptions在app delegate中假设你的初始视图控制器是TabbarController中的signed。

if Auth.auth().currentUser == nil {
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        self.window?.rootViewController = rootController
    }

    return true

在注册视图控制器中:

@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}

MyTabThreeViewController

 //Remove user credentials
guard let appDel = UIApplication.shared.delegate as? AppDelegate else { return }
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        appDel.window?.rootViewController = rootController