Thực tiễn tốt nhất cho màn hình đăng nhập Storyboard, xử lý xóa dữ liệu khi đăng xuất


290

Tôi đang xây dựng một ứng dụng iOS bằng Storyboard. Bộ điều khiển xem gốc là Bộ điều khiển Tab Bar. Tôi đang tạo quá trình đăng nhập / đăng xuất và nó hầu như hoạt động tốt, nhưng tôi đã gặp một số vấn đề. Tôi cần biết cách TỐT NHẤT để thiết lập tất cả điều này.

Tôi muốn thực hiện như sau:

  1. Hiển thị màn hình đăng nhập lần đầu tiên ứng dụng được khởi chạy. Khi họ đăng nhập, hãy chuyển đến tab đầu tiên của Trình điều khiển thanh tab.
  2. Bất cứ khi nào họ khởi chạy ứng dụng sau đó, hãy kiểm tra xem họ đã đăng nhập chưa, và chuyển thẳng đến tab đầu tiên của Trình điều khiển thanh tab gốc.
  3. Khi họ nhấp vào nút đăng xuất theo cách thủ công, hiển thị màn hình đăng nhập và xóa tất cả dữ liệu khỏi bộ điều khiển xem.

Những gì tôi đã làm cho đến nay là đặt trình điều khiển chế độ xem gốc thành Trình điều khiển thanh tab và tạo một phân biệt tùy chỉnh cho trình điều khiển chế độ xem Đăng nhập của tôi. Trong lớp Trình điều khiển thanh Tab của tôi, tôi kiểm tra xem chúng có được đăng nhập bên trong viewDidAppearphương thức hay không và thực hiện lệnh segue:[self performSegueWithIdentifier:@"pushLogin" sender:self];

Tôi cũng thiết lập thông báo khi cần thực hiện hành động đăng xuất: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Khi đăng xuất, tôi xóa thông tin đăng nhập khỏi Keychain, chạy [self setSelectedIndex:0]và thực hiện segue để hiển thị lại bộ điều khiển xem đăng nhập.

Tất cả đều hoạt động tốt, nhưng tôi tự hỏi: logic này có nên xuất hiện trong AppDelegate không? Tôi cũng có hai vấn đề:

  • Lần đầu tiên họ khởi chạy ứng dụng , Trình điều khiển Tab Bar sẽ hiển thị nhanh trước khi thực hiện phân tách. Tôi đã thử chuyển mã sang viewWillAppearnhưng segue sẽ không hoạt động sớm.
  • Khi họ đăng xuất, tất cả dữ liệu vẫn nằm trong tất cả các bộ điều khiển xem. Nếu họ đăng nhập vào tài khoản mới, dữ liệu tài khoản cũ vẫn được hiển thị cho đến khi họ làm mới. Tôi cần một cách để xóa điều này một cách dễ dàng khi đăng xuất.

Tôi đang mở để làm lại điều này. Tôi đã xem xét việc tạo màn hình đăng nhập thành trình điều khiển xem gốc hoặc tạo trình điều khiển điều hướng trong AppDelegate để xử lý mọi thứ ... Tôi chỉ không chắc phương pháp tốt nhất là gì vào thời điểm này.


Bạn có trình bày bộ điều khiển xem đăng nhập như phương thức?
vokilam

@TrevorGehman - có thể thêm ảnh trên bảng phân cảnh của bạn
rohan k shah

Tôi đã gửi một câu trả lời với các chi tiết về những gì tôi đã làm. Nó tương tự như một số câu trả lời khác được cung cấp, đặc biệt là @bhavya kothari.
Trevor Gehman

Đối với màn hình đăng nhập, AuthNavulation có thể hữu ích. Nó tổ chức trình bày màn hình đăng nhập nếu cần và cũng hỗ trợ tự động đăng nhập.
Codey

Một trong những vấn đề rất cơ bản mà hầu như luôn luôn giải quyết nhưng đồng thời cảm thấy như thể đã được thực hiện tốt hơn
amar

Câu trả lời:


311

Bảng phân cảnh của bạn sẽ trông như thế này

Trong appDelegate.m của bạn bên trong didFinishLaunchingWithOptions của bạn

//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;
}

Trong tệp SignUpViewControll.m

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

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

Trong tệp MyTabThreeViewControll.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;

}

Phiên bản Swift 4

didFinishLaunchingWithOptions trong đại biểu ứng dụng giả sử bộ điều khiển xem ban đầu của bạn là TabbarContoder đã đăng nhập.

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

    return true

Trong Trình điều khiển xem đăng ký:

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

MyTabThreeViewCont điều khiển

 //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

Bạn đã quên xóa xác thực bool khỏi userDefaults sau khi đăng xuất
CodeLover

28
-1 để sử dụng AppDelegatebên trong UIViewControllervà thiết lập window.rootViewControllerở đó. Tôi không coi đây là một "cách thực hành tốt nhất".
derpoliuk

2
Không muốn đưa ra -1mà không đăng một câu trả lời: stackoverflow.com/a/30664935/1226304
derpoliuk

1
Tôi đang cố gắng thực hiện việc này nhanh chóng trên iOS8 nhưng tôi gặp phải lỗi sau khi ứng dụng kích hoạt và màn hình đăng nhập hiển thị: "Cuộc gọi không cân bằng để bắt đầu / kết thúc chuyển đổi xuất hiện". Tôi đã nhận thấy rằng khi ứng dụng tải màn hình đăng nhập hiển thị, nhưng cũng có tab đầu tiên trên bộ điều khiển thanh tab cũng được tải. Xác nhận điều này thông qua println () trong viewdidload. Gợi ý?
Alex Lacayo

1
chơi lô tô! -2. -1 cho AppDelegatebên trong UIViewController-1 cho Lưu trữ khóa đăng nhập NSUserDefaults. Nó rất không an toàn cho loại dữ liệu đó!
skywinder

97

Đây là những gì tôi đã làm để hoàn thành mọi thứ. Điều duy nhất bạn cần xem xét ngoài việc này là (a) quá trình đăng nhập và (b) nơi bạn đang lưu trữ dữ liệu ứng dụng của mình (trong trường hợp này, tôi đã sử dụng một singleton).

Bảng phân cảnh hiển thị trình điều khiển xem đăng nhập và trình điều khiển tab chính

Như bạn có thể thấy, trình điều khiển xem gốc là Trình điều khiển tab chính của tôi . Tôi đã làm điều này bởi vì sau khi người dùng đã đăng nhập, tôi muốn ứng dụng khởi chạy trực tiếp vào tab đầu tiên. (Điều này tránh mọi "nhấp nháy" trong đó chế độ xem đăng nhập hiển thị tạm thời.)

AppDelegate.m

Trong tệp này, tôi kiểm tra xem người dùng đã đăng nhập chưa. Nếu không, tôi đẩy trình điều khiển xem đăng nhập. Tôi cũng xử lý quá trình đăng xuất, nơi tôi xóa dữ liệu và hiển thị chế độ xem đăng nhập.

- (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];

}

Đăng nhậpViewControll.m

Ở đây, nếu đăng nhập thành công, tôi chỉ cần bỏ chế độ xem và gửi thông báo.

-(void) loginWasSuccessful
{

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

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

}

2
Bạn sử dụng thông báo để làm gì?
cuộc nổi loạn

1
@BFeher đã đúng. Tôi đã sử dụng thông báo để kích hoạt kéo dữ liệu mới. Bạn có thể sử dụng nó để làm bất cứ điều gì bạn muốn, nhưng trong trường hợp của tôi, tôi cần được thông báo rằng đăng nhập thành công và cần có dữ liệu mới.
Trevor Gehman

24
Trong iOS 8.1 (và có lẽ 8.0, chưa được thử nghiệm), tính năng này không còn hoạt động trơn tru. Trình điều khiển View ban đầu nhấp nháy trong giây lát.
BFeher

7
Có một phiên bản Swift của phương pháp này?
Seano

9
@Julian Trong iOS 8, tôi thay thế hai dòng [self.window makeKeyAndVisible]; [self.window.rootViewController presentViewController:viewController animated:animated completion:nil];bằng self.window.rootViewController = viewController;để ngăn nhấp nháy. Để animate chỉ cần bọc nó trong một[UIView transitionWithView...];
BFeher

20

EDIT: Thêm hành động đăng xuất.

nhập mô tả hình ảnh ở đây

1. Trước hết hãy chuẩn bị tệp đại biểu ứng dụng

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. Tạo một lớp có tên Người dùng.

Người dùng

#import <Foundation/Foundation.h>

@interface User : NSObject

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

@end

Người dùng

#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. Tạo một bộ điều khiển mới RootViewControll và được kết nối với chế độ xem đầu tiên, nơi nút đăng nhập trực tiếp. Thêm một ID Storyboard: "initView".

RootViewControll.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

RootViewControll.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. Tạo một bộ điều khiển mới LoginViewContaptor và được kết nối với giao diện đăng nhập.

Đăng nhậpViewControll.h

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

@interface LoginViewController : UIViewController

Đăng nhậpViewControll.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. Cuối cùng, thêm một bộ điều khiển mới ProfileViewContaptor và được kết nối với chế độ xem hồ sơ trong tabViewControll.

ProfileViewControll.h

#import <UIKit/UIKit.h>

@interface ProfileViewController : UIViewController

@end

ProfileViewControll.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 là một dự án mẫu để được trợ giúp thêm.


3
dự án mẫu đã giúp tôi rất nhiều để hiểu khái niệm đăng nhập n đăng xuất .. cảm ơn rất nhiều :)
Dave

16

Tôi không thích câu trả lời của bhavya vì sử dụng AppDelegatebên trong Bộ điều khiển xem và cài đặt rootViewControllerkhông có hoạt hình. Và câu trả lời của Trevor có vấn đề với bộ điều khiển xem flash trên iOS8.

CẬP NHẬT 18/07/2015

AppDelegate bên trong Bộ điều khiển xem:

Thay đổi trạng thái AppDelegate (thuộc tính) bên trong bộ điều khiển xem phá vỡ đóng gói.

Hệ thống phân cấp các đối tượng rất đơn giản trong mọi dự án iOS:

AppDelegate (sở hữu windowrootViewController)

ViewContoder (sở hữu view)

Không sao, các đối tượng từ các đối tượng thay đổi ở trên cùng, vì họ đang tạo ra chúng. Nhưng sẽ không ổn nếu các đối tượng ở phía dưới thay đổi các đối tượng trên đầu chúng (Tôi đã mô tả một số nguyên tắc lập trình / OOP cơ bản: DIP (Nguyên tắc đảo ngược phụ thuộc: mô đun cấp cao không được phụ thuộc vào mô đun cấp thấp, nhưng chúng nên phụ thuộc vào trừu tượng) ).

Nếu bất kỳ đối tượng nào sẽ thay đổi bất kỳ đối tượng nào trong hệ thống phân cấp này, sớm hay muộn sẽ có một mớ hỗn độn trong mã. Nó có thể ổn với các dự án nhỏ nhưng không có gì thú vị khi tìm hiểu mớ hỗn độn này trên các dự án bit =]

CẬP NHẬT 18/07/2015

Tôi sao chép hình ảnh động của bộ điều khiển phương thức bằng cách sử dụng UINavigationController(tl; dr: kiểm tra dự án ).

Tôi đang sử dụng UINavigationControllerđể trình bày tất cả các bộ điều khiển trong ứng dụng của mình. Ban đầu tôi hiển thị trình điều khiển xem đăng nhập trong ngăn xếp điều hướng với hoạt hình đẩy / pop đơn giản. Hơn tôi quyết định thay đổi nó thành phương thức với những thay đổi tối thiểu.

Làm thế nào nó hoạt động:

  1. Bộ điều khiển xem ban đầu (hoặc self.window.rootViewController) là UINavestionContaptor với ProgressViewContaptor là a rootViewController. Tôi đang hiển thị ProgressViewControll vì DataModel có thể mất một chút thời gian để khởi tạo vì nó tạo ra ngăn xếp dữ liệu cốt lõi như trong bài viết này (tôi thực sự thích cách tiếp cận này).

  2. AppDelegate chịu trách nhiệm nhận cập nhật trạng thái đăng nhập.

  3. DataModel xử lý đăng nhập / đăng xuất của người dùng và AppDelegate đang quan sát userLoggedIntài sản của nó thông qua KVO. Có thể cho rằng không phải là phương pháp tốt nhất để làm điều này nhưng nó hiệu quả với tôi. (Tại sao KVO là xấu, bạn có thể kiểm tra trong này hoặc bài viết này (Why Not Sử dụng Notifications? Phần).

  4. ModalDismissAnimator và ModalPftimeAnimator được sử dụng để tùy chỉnh hoạt hình đẩy mặc định.

Làm thế nào logic hoạt hình hoạt động:

  1. AppDelegate tự đặt mình là đại biểu của self.window.rootViewController(đó là UINavestionControll).

  2. AppDelegate trả về một trong số các hoạt hình -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]nếu cần thiết.

  3. Hoạt hình thực hiện -transitionDuration:-animateTransition:phương pháp. -[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]];
         }];
    }

Dự án thử nghiệm là đây .


3
Cá nhân tôi không có vấn đề gì với Bộ điều khiển xem biết AppDelegate(Tôi rất muốn hiểu lý do tại sao bạn làm như vậy) - nhưng nhận xét của bạn về việc thiếu hình ảnh động là rất hợp lệ. Điều đó có thể được giải quyết bằng câu trả lời này: stackoverflow.com/questions/8053832/
trộm

2
@HughHughTeotl Cảm ơn bạn đã bình luận và liên kết. Tôi cập nhật câu trả lời của tôi.
derpoliuk 18/07/2015

1
@derpoliuk nếu bộ điều khiển xem cơ sở của tôi là UITabBarControll thì sao? Tôi không thể đẩy nó trong UINavestionControll.
Giorgio

@Giorgio, đó là một câu hỏi thú vị, tôi đã không sử dụng UITabBarControllertrong một thời gian rất dài. Có lẽ tôi nên bắt đầu với cách tiếp cận cửa sổ thay vì thao tác với bộ điều khiển xem.
derpoliuk

11

Đây là giải pháp Swifty của tôi cho bất kỳ người xem nào trong tương lai.

1) Tạo giao thức để xử lý cả chức năng đăng nhập và đăng xuất:

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

2) Mở rộng giao thức đã nói và cung cấp chức năng ở đây để đăng xuất:

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) Sau đó, tôi có thể tuân thủ AppDelegate của mình với giao thức LoginFlowHandler và gọi handleLoginkhi khởi động:

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
    }

}

Từ đây, tiện ích mở rộng giao thức của tôi sẽ xử lý logic hoặc xác định xem người dùng nếu đăng nhập / đăng xuất, sau đó thay đổi cửa sổ rootViewControll cho phù hợp!


Không chắc chắn liệu tôi có ngu ngốc không, nhưng AppDelegate không tuân thủ LoginFlowHandler. Tui bỏ lỡ điều gì vậy? Ngoài ra, tôi đoán mã này chỉ quản lý đăng nhập khi khởi động. Làm cách nào để quản lý đăng xuất khỏi bộ điều khiển xem?
luke

@luke vì tất cả logic được triển khai trong phần mở rộng nên không cần triển khai nó trong AppDelegate. Đó là những gì tuyệt vời trong Giao thức mở rộng.
shannoga

1
Xin lỗi @sirFunkenstine, đó là một lớp tùy chỉnh mà tôi đã tạo để hiển thị một ví dụ về cách người ta sẽ kiểm tra bộ đệm ứng dụng của họ để kiểm tra người dùng đã đăng nhập trước đó hay chưa. Đây AppStatedo đó thực hiện sẽ phụ thuộc vào cách bạn đang tiết kiệm dữ liệu người dùng của bạn vào đĩa.
Harry Bloom

@HarryBloom người ta sẽ sử dụng handleLogoutchức năng như thế nào?
nithinisreddy

1
Xin chào @nithinisreddy - để gọi chức năng handleLogout, bạn sẽ cần tuân thủ lớp mà bạn đang gọi từ LoginFlowHandlergiao thức. Sau đó, bạn sẽ nhận được phạm vi để có thể gọi phương thức handleLogout. Xem bước 3 của tôi để biết ví dụ về cách tôi đã làm điều đó cho lớp AppDelegate.
Harry Bloom

8

Làm điều này từ đại biểu ứng dụng KHÔNG được khuyến khích. AppDelegate quản lý vòng đời ứng dụng liên quan đến khởi chạy, tạm dừng, chấm dứt, v.v. Tôi đề nghị làm điều này từ bộ điều khiển xem ban đầu của bạn trong viewDidAppear. Bạn có thể self.presentViewControllerself.dismissViewControllertừ trình điều khiển xem đăng nhập. Lưu trữ một boolkhóa NSUserDefaultsđể xem nếu nó khởi chạy lần đầu tiên.


2
Chế độ xem có nên xuất hiện (hiển thị cho người dùng) trong `viewDidAppear 'không? Điều này vẫn sẽ tạo ra một nhấp nháy.
Mark13426

2
Không phải là một câu trả lời. Và "Lưu trữ khóa bool trong NSUserDefaults để xem nếu nó khởi chạy lần đầu tiên." Rất nguy hiểm cho loại dữ liệu đó.
skywinder

6

Tạo ** LoginViewControll ** và ** TabBarControll **.

Sau khi tạo LoginViewControllerTabBarController , chúng ta cần thêm một StoryboardID là “ loginViewController ” và “ tabBarController ” tương ứng.

Sau đó, tôi thích tạo cấu trúc Constant :

struct Constants {
    struct StoryboardID {
        static let signInViewController = "SignInViewController"
        static let mainTabBarController = "MainTabBarController"
    }

    struct kUserDefaults {
        static let isSignIn = "isSignIn"
    }
}

Trong LoginViewControll thêm IBAction :

@IBAction func tapSignInButton(_ sender: UIButton) {
    UserDefaults.standard.set(true, forKey: Constants.kUserDefaults.isSignIn)
    Switcher.updateRootViewController()
}

Trong ProfileViewControll thêm IBAction :

@IBAction func tapSignOutButton(_ sender: UIButton) {
    UserDefaults.standard.set(false, forKey: Constants.kUserDefaults.isSignIn)
    Switcher.updateRootViewController()
}

Trong AppDelegate, thêm dòng mã trong didFinishLaunchingWithOptions :

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

    Switcher.updateRootViewController()

    return true
}

Cuối cùng tạo lớp Switcher :

import UIKit

class Switcher {

    static func updateRootViewController() {

        let status = UserDefaults.standard.bool(forKey: Constants.kUserDefaults.isSignIn)
        var rootViewController : UIViewController?

        #if DEBUG
        print(status)
        #endif

        if (status == true) {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.mainTabBarController) as! MainTabBarController
            rootViewController = mainTabBarController
        } else {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
            rootViewController = signInViewController
        }

        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.window?.rootViewController = rootViewController

    }

}

Đó là tất cả!


Có sự khác biệt nào mà trình điều khiển xem là ban đầu trong bảng phân cảnh không? Trong ảnh đã thêm của bạn, tôi có thể thấy rằng bạn có tùy chọn "là Trình điều khiển xem ban đầu" được chọn trên Trình điều khiển thanh tab. Trong AppDelegate u chuyển bộ điều khiển xem gốc chính vì vậy tôi đoán nó không thành vấn đề, phải không?
ShadowToD

@iAleksandr Vui lòng cập nhật câu trả lời cho iOS 13. Vì câu trả lời hiện tại của SceneDelegate không hoạt động.
Nitesh

5

Trong Xcode 7, bạn có thể có nhiều StoryBoards. Sẽ tốt hơn nếu bạn có thể giữ luồng Đăng nhập trong một bảng phân cảnh riêng.

Điều này có thể được thực hiện bằng cách sử dụng SELECT VIEWCONTROLLER> Editor> Refactor to Storyboard

Và đây là phiên bản Swift để đặt chế độ xem là RootViewContoller-

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

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

3

Tôi sử dụng điều này để kiểm tra lần ra mắt đầu tiên:

- (NSInteger) checkForFirstLaunch
{
    NSInteger result = 0; //no first launch

    // Get current version ("Bundle Version") from the default Info.plist file
    NSString *currentVersion = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
    NSArray *prevStartupVersions = [[NSUserDefaults standardUserDefaults] arrayForKey:@"prevStartupVersions"];
    if (prevStartupVersions == nil)
    {
        // Starting up for first time with NO pre-existing installs (e.g., fresh
        // install of some version)
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:currentVersion] forKey:@"prevStartupVersions"];
        result = 1; //first launch of the app
    } else {
        if (![prevStartupVersions containsObject:currentVersion])
        {
            // Starting up for first time with this version of the app. This
            // means a different version of the app was alread installed once
            // and started.
            NSMutableArray *updatedPrevStartVersions = [NSMutableArray arrayWithArray:prevStartupVersions];
            [updatedPrevStartVersions addObject:currentVersion];
            [[NSUserDefaults standardUserDefaults] setObject:updatedPrevStartVersions forKey:@"prevStartupVersions"];
            result = 2; //first launch of this version of the app
        }
    }

    // Save changes to disk
    [[NSUserDefaults standardUserDefaults] synchronize];

    return result;
}

(nếu người dùng xóa ứng dụng và cài đặt lại, ứng dụng sẽ được tính như lần khởi chạy đầu tiên)

Trong AppDelegate, tôi kiểm tra lần khởi chạy đầu tiên và tạo bộ điều khiển điều hướng với màn hình đăng nhập (đăng nhập và đăng ký), mà tôi đặt lên trên cửa sổ chính hiện tại:

[self.window makeKeyAndVisible];

if (firstLaunch == 1) {
    UINavigationController *_login = [[UINavigationController alloc] initWithRootViewController:loginController];
    [self.window.rootViewController presentViewController:_login animated:NO completion:nil];
}

Vì đây là trên cùng của trình điều khiển chế độ xem thông thường, nó độc lập với phần còn lại của ứng dụng của bạn và bạn chỉ có thể loại bỏ trình điều khiển chế độ xem, nếu bạn không cần nó nữa. Và bạn cũng có thể trình bày chế độ xem theo cách này, nếu người dùng nhấn nút thủ công.

BTW: Tôi lưu dữ liệu đăng nhập từ người dùng của mình như thế này:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"com.youridentifier" accessGroup:nil];
[keychainItem setObject:password forKey:(__bridge id)(kSecValueData)];
[keychainItem setObject:email forKey:(__bridge id)(kSecAttrAccount)];

Đối với đăng xuất: Tôi đã chuyển khỏi CoreData (quá chậm) và sử dụng NSArrays và NSDictaries để quản lý dữ liệu của mình ngay bây giờ. Thoát ra chỉ có nghĩa là để trống những mảng và từ điển. Thêm vào đó, tôi đảm bảo đặt dữ liệu của mình trong viewWillAppear.

Đó là nó.


0

Tôi cũng ở trong tình trạng giống như bạn và giải pháp tôi tìm thấy để làm sạch dữ liệu là xóa tất cả nội dung CoreData mà bộ điều khiển xem của tôi dựa vào để rút ra thông tin. Nhưng tôi vẫn thấy cách tiếp cận này rất tệ, tôi nghĩ rằng một cách thanh lịch hơn để làm điều này có thể được thực hiện mà không cần bảng phân cảnh và chỉ sử dụng mã để quản lý các chuyển đổi giữa các bộ điều khiển xem.

Tôi đã tìm thấy dự án này tại Github, thực hiện tất cả những thứ này chỉ bằng mã và nó khá dễ hiểu. Họ sử dụng menu phụ giống như Facebook và những gì họ làm là thay đổi bộ điều khiển xem trung tâm tùy thuộc vào việc người dùng có đăng nhập hay không. Khi người dùng đăng xuất, appDelegatexóa dữ liệu khỏi CoreData và đặt lại bộ điều khiển chế độ xem chính vào màn hình đăng nhập.


0

Tôi đã có một vấn đề tương tự để giải quyết trong một ứng dụng và tôi đã sử dụng phương pháp sau. Tôi đã không sử dụng thông báo để xử lý điều hướng.

Tôi có ba bảng phân cảnh trong ứng dụng.

  1. Bảng phân cảnh màn hình Splash - để khởi tạo ứng dụng và kiểm tra xem người dùng đã đăng nhập chưa
  2. Bảng phân cảnh đăng nhập - để xử lý luồng đăng nhập của người dùng
  3. Bảng phân cảnh thanh tab - để hiển thị nội dung ứng dụng

Bảng phân cảnh ban đầu của tôi trong ứng dụng là bảng phân cảnh màn hình Splash. Tôi có bộ điều khiển điều hướng làm gốc của bảng điều khiển thanh đăng nhập và thanh tab để xử lý các điều hướng xem của bộ điều khiển.

Tôi đã tạo một lớp Điều hướng để xử lý điều hướng ứng dụng và nó trông như thế này:

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)

           }

       }

   }

}

Hãy xem xét các tình huống có thể xảy ra:

  • Khởi chạy ứng dụng đầu tiên; Màn hình Splash sẽ được tải trong đó tôi kiểm tra xem người dùng đã đăng nhập chưa. Sau đó, màn hình đăng nhập sẽ được tải bằng lớp Navigator như sau;

Vì tôi có bộ điều khiển điều hướng làm gốc, tôi khởi tạo bộ điều khiển điều hướng làm bộ điều khiển xem ban đầu.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Điều này loại bỏ bảng phân cảnh slpash khỏi thư mục gốc của cửa sổ ứng dụng và thay thế nó bằng bảng phân cảnh đăng nhập.

Từ bảng phân cảnh đăng nhập, khi người dùng đăng nhập thành công, tôi lưu dữ liệu người dùng vào Mặc định của người dùng và khởi tạo một đĩa đơn UserData để truy cập chi tiết người dùng. Sau đó, bảng phân cảnh thanh Tab được tải bằng phương pháp điều hướng.

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

Navigator.moveTo(tabBarNav, from: self)

Bây giờ người dùng đăng xuất từ ​​màn hình cài đặt trong thanh tab. Tôi xóa tất cả dữ liệu người dùng đã lưu và điều hướng đến màn hình đăng nhập.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • Người dùng đã đăng nhập và buộc giết ứng dụng

Khi người dùng khởi chạy ứng dụng, màn hình Splash sẽ được tải. Tôi kiểm tra xem người dùng đã đăng nhập và truy cập dữ liệu người dùng từ Mặc định của người dùng. Sau đó, khởi tạo singleton UserData và hiển thị thanh tab thay vì màn hình đăng nhập.


-1

Cảm ơn giải pháp của bhavya. Đã có hai câu trả lời về swift, nhưng chúng không còn nguyên vẹn. Tôi đã làm điều đó trong swift3.Below là mã chính.

Trong AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    // seclect the mainStoryBoard entry by whthere user is login.
    let userDefaults = UserDefaults.standard

    if let isLogin: Bool = userDefaults.value(forKey:Common.isLoginKey) as! Bool? {
        if (!isLogin) {
            self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LogIn")
        }
   }else {
        self.window?.rootViewController = mainStoryboard.instantiateViewController(withIdentifier: "LogIn")
   }

    return true
}

Trong SignUpViewControll.swift

@IBAction func userLogin(_ sender: UIButton) {
    //handle your login work
    UserDefaults.standard.setValue(true, forKey: Common.isLoginKey)
    let delegateTemp = UIApplication.shared.delegate
    delegateTemp?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Main")
}

Trong hàm logOutAction

@IBAction func logOutAction(_ sender: UIButton) {
    UserDefaults.standard.setValue(false, forKey: Common.isLoginKey)
    UIApplication.shared.delegate?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
}

Xin chào Eli. Câu hỏi bạn đã trả lời đã có một vài câu trả lời thực sự tốt. Khi bạn quyết định trả lời một câu hỏi như vậy, hãy đảm bảo giải thích tại sao câu trả lời của bạn tốt hơn câu trả lời rất hay đã được đăng.
Noel Widmer

Chào Noel. Tôi nhận thấy các câu trả lời khác cho nhanh chóng. Nhưng tôi coi câu trả lời không còn nguyên vẹn. Vì vậy, tôi gửi câu trả lời của tôi về phiên bản swift3. Nó sẽ giúp ích cho lập trình viên nhanh chóng mới. Cảm ơn bạn! @Noel Widmer.
WangYang

Bạn có thể thêm lời giải thích đó ở đầu bài viết của bạn? Bằng cách đó mọi người có thể thấy ngay lợi ích của câu trả lời của bạn. Có một thời gian tốt trên SO! :)
Noel Widmer

1
Xe tăng cho đề xuất của bạn. Tôi đã thêm lời giải thích. Cảm ơn một lần nữa. @ Noel Widmer.
WangYang

Giải pháp mơ hồ không làm nổi bật việc sử dụng từ khóa 'Chung'.
Samarey

-3

nhập mô tả hình ảnh ở đây

Trong ứng dụng 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;

}

xem bộ điều khiển.m Trong chế độ xem đã tải

- (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];

}

Trong hành động nút đăng xuất

-(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];
});}

Tại sao cần phải thêm một số chức năng vào tệp ViewContoder.m ??
Eesha

@Eesha Ông đã thêm một mục nút "đăng xuất" vào TabBar. Tôi đoán hình ảnh bị thiếu khác bạn có thể đã nhìn thấy nó.
helloWorld

Lưu trữ khóa đăng nhập trong NSUserDefaultsrất rất không an toàn cho loại dữ liệu đó!
skywinder
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.