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


当前回答

在这里输入图像描述

在App Delegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                     forBarMetrics:UIBarMetricsDefault];

NSString *identifier;
BOOL isSaved = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginSaved"];
if (isSaved)
{
    //identifier=@"homeViewControllerId";
    UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window];
    UITabBarController *tabBarVC =
    [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"];
    mainWindow.rootViewController=tabBarVC;
}
else
{


    identifier=@"loginViewControllerId";
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:identifier];

    UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:screen];

    self.window.rootViewController = navigationController;
    [self.window makeKeyAndVisible];

}

return YES;

}

视图controller.m 视图中didload

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:@selector(logoutButtonClicked:)];
[self.navigationItem setLeftBarButtonItem:barButton];

}

在登出按钮动作

-(void)logoutButtonClicked:(id)sender{

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Do you want to logout?" preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
           NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:NO forKey:@"loginSaved"];
           [[NSUserDefaults standardUserDefaults] synchronize];
      AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:@"loginViewControllerId"];
    [appDelegate.window setRootViewController:screen];
}]];


[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self dismissViewControllerAnimated:YES completion:nil];
}]];

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:alertController animated:YES completion:nil];
});}

其他回答

下面是我最终完成所有事情的方法。除此之外,你需要考虑的唯一一件事是(a)登录过程和(b)存储应用数据的位置(在本例中,我使用了单例)。

如你所见,根视图控制器是我的主选项卡控制器。我这样做是因为用户登录后,我希望应用程序直接启动到第一个选项卡。(这避免了登录视图临时显示的任何“闪烁”。)

AppDelegate.m

在这个文件中,我检查用户是否已经登录。如果不是,我就推登录视图控制器。我还处理注销过程,清除数据并显示登录视图。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    // Show login view if not logged in already
    if(![AppData isLoggedIn]) {
        [self showLoginScreen:NO];
    }

    return YES;
}

-(void) showLoginScreen:(BOOL)animated
{

    // Get login screen from storyboard and present it
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    LoginViewController *viewController = (LoginViewController *)[storyboard instantiateViewControllerWithIdentifier:@"loginScreen"];
    [self.window makeKeyAndVisible];
    [self.window.rootViewController presentViewController:viewController
                                             animated:animated
                                           completion:nil];
}

-(void) logout
{
    // Remove data from singleton (where all my app data is stored)
    [AppData clearData];

   // Reset view controller (this will quickly clear all the views)
   UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
   MainTabControllerViewController *viewController = (MainTabControllerViewController *)[storyboard instantiateViewControllerWithIdentifier:@"mainView"];
   [self.window setRootViewController:viewController];

   // Show login screen
   [self showLoginScreen:NO];

}

LoginViewController。m

在这里,如果登录成功,我只需取消视图并发送通知。

-(void) loginWasSuccessful
{

     // Send notification
     [[NSNotificationCenter defaultCenter] postNotificationName:@"loginSuccessful" object:self];

     // Dismiss login screen
     [self dismissViewControllerAnimated:YES completion:nil];

}

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

编辑:添加注销操作。

1. 首先准备app委托文件

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic) BOOL authenticated;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "User.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    User *userObj = [[User alloc] init];
    self.authenticated = [userObj userAuthenticated];

    return YES;
}

2. 创建一个名为User的类。

User.h

#import <Foundation/Foundation.h>

@interface User : NSObject

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password;
- (void)logout;
- (BOOL)userAuthenticated;

@end

User.m

#import "User.h"

@implementation User

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password{

    // Validate user here with your implementation
    // and notify the root controller
    [[NSNotificationCenter defaultCenter] postNotificationName:@"loginActionFinished" object:self userInfo:nil];
}

- (void)logout{
    // Here you can delete the account
}

- (BOOL)userAuthenticated {

    // This variable is only for testing
    // Here you have to implement a mechanism to manipulate this
    BOOL auth = NO;

    if (auth) {
        return YES;
    }

    return NO;
}

3.创建一个新的控制器RootViewController,并连接到登录按钮所在的第一个视图。同时添加一个故事板ID: "initialView"。

RootViewController.h

#import <UIKit/UIKit.h>
#import "LoginViewController.h"

@protocol LoginViewProtocol <NSObject>

- (void)dismissAndLoginView;

@end

@interface RootViewController : UIViewController

@property (nonatomic, weak) id <LoginViewProtocol> delegate;
@property (nonatomic, retain) LoginViewController *loginView;


@end

RootViewController.m

#import "RootViewController.h"

@interface RootViewController ()

@end

@implementation RootViewController

@synthesize loginView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)loginBtnPressed:(id)sender {

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loginActionFinished:)
                                                 name:@"loginActionFinished"
                                               object:loginView];

}

#pragma mark - Dismissing Delegate Methods

-(void) loginActionFinished:(NSNotification*)notification {

    AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    authObj.authenticated = YES;

    [self dismissLoginAndShowProfile];
}

- (void)dismissLoginAndShowProfile {
    [self dismissViewControllerAnimated:NO completion:^{
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UITabBarController *tabView = [storyboard instantiateViewControllerWithIdentifier:@"profileView"];
        [self presentViewController:tabView animated:YES completion:nil];
    }];


}

@end

4. 创建一个新的控制器LoginViewController,并连接到登录视图。

LoginViewController.h

#import <UIKit/UIKit.h>
#import "User.h"

@interface LoginViewController : UIViewController

LoginViewController。m

#import "LoginViewController.h"
#import "AppDelegate.h"

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)submitBtnPressed:(id)sender {
    User *userObj = [[User alloc] init];

    // Here you can get the data from login form
    // and proceed to authenticate process
    NSString *username = @"username retrieved through login form";
    NSString *password = @"password retrieved through login form";
    [userObj loginWithUsername:username andPassword:password];
}

@end

5. 最后添加一个新的控制器ProfileViewController,并连接到tabViewController中的配置文件视图。

ProfileViewController.h

#import <UIKit/UIKit.h>

@interface ProfileViewController : UIViewController

@end

ProfileViewController.m

#import "ProfileViewController.h"
#import "RootViewController.h"
#import "AppDelegate.h"
#import "User.h"

@interface ProfileViewController ()

@end

@implementation ProfileViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

}

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if(![(AppDelegate*)[[UIApplication sharedApplication] delegate] authenticated]) {

        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

        RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
        [initView setModalPresentationStyle:UIModalPresentationFullScreen];
        [self presentViewController:initView animated:NO completion:nil];
    } else{
        // proceed with the profile view
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)logoutAction:(id)sender {

   User *userObj = [[User alloc] init];
   [userObj logout];

   AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
   authObj.authenticated = NO;

   UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

   RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
   [initView setModalPresentationStyle:UIModalPresentationFullScreen];
   [self presentViewController:initView animated:NO completion:nil];

}

@end

LoginExample是一个提供额外帮助的示例项目。

在这里输入图像描述

在App Delegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                     forBarMetrics:UIBarMetricsDefault];

NSString *identifier;
BOOL isSaved = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginSaved"];
if (isSaved)
{
    //identifier=@"homeViewControllerId";
    UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window];
    UITabBarController *tabBarVC =
    [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"];
    mainWindow.rootViewController=tabBarVC;
}
else
{


    identifier=@"loginViewControllerId";
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:identifier];

    UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:screen];

    self.window.rootViewController = navigationController;
    [self.window makeKeyAndVisible];

}

return YES;

}

视图controller.m 视图中didload

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:@selector(logoutButtonClicked:)];
[self.navigationItem setLeftBarButtonItem:barButton];

}

在登出按钮动作

-(void)logoutButtonClicked:(id)sender{

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Do you want to logout?" preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
           NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:NO forKey:@"loginSaved"];
           [[NSUserDefaults standardUserDefaults] synchronize];
      AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:@"loginViewControllerId"];
    [appDelegate.window setRootViewController:screen];
}]];


[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self dismissViewControllerAnimated:YES completion:nil];
}]];

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:alertController animated:YES completion:nil];
});}

我在一个应用程序中也有类似的问题要解决,我使用了以下方法。我没有使用通知来处理导航。

我在应用程序中有三个故事板。

启动画面故事板-用于应用程序初始化和检查用户是否已经登录 登录故事板——用于处理用户登录流程 标签栏故事板-用于显示应用程序内容

我在应用程序中的初始故事板是启动画面故事板。 我有导航控制器作为根登录和选项卡栏故事板处理视图控制器导航。

我创建了一个Navigator类来处理应用程序导航,它看起来像这样:

class Navigator: NSObject {

   static func moveTo(_ destinationViewController: UIViewController, from sourceViewController: UIViewController, transitionStyle: UIModalTransitionStyle? = .crossDissolve, completion: (() -> ())? = nil) {
       

       DispatchQueue.main.async {

           if var topController = UIApplication.shared.keyWindow?.rootViewController {

               while let presentedViewController = topController.presentedViewController {

                   topController = presentedViewController

               }

               
               destinationViewController.modalTransitionStyle = (transitionStyle ?? nil)!

               sourceViewController.present(destinationViewController, animated: true, completion: completion)

           }

       }

   }

}

让我们来看看可能的场景:

首次应用发行; 启动屏幕将加载,我检查用户是否已经登录。然后登录屏幕将使用Navigator类加载,如下所示;

因为我有导航控制器作为根,我实例化导航控制器作为初始视图控制器。

let loginSB = UIStoryboard(name: "splash", bundle: nil)

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

这将从应用程序窗口的根目录中删除slpash故事板,并将其替换为登录故事板。

在登录故事板中,当用户成功登录时,我将用户数据保存到user Defaults中,并初始化UserData单例来访问用户详细信息。然后使用navigator方法加载标签栏故事板。

Let tabBarSB = UIStoryboard(name: "tabBar", bundle: nil)
let tabBarNav = tabBarSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(tabBarNav, from: self)

现在用户从标签栏的设置屏幕中退出。我清除所有保存的用户数据,并导航到登录屏幕。

let loginSB = UIStoryboard(name: "splash", bundle: nil)

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

用户登录并强制杀死应用程序

当用户启动应用程序时,启动画面将被加载。我检查用户是否已登录,并从用户默认中访问用户数据。然后初始化UserData单例,显示标签栏而不是登录屏幕。