Programing

스토리 보드 로그인 화면 모범 사례, 로그 아웃시 데이터 지우기 처리

lottogame 2020. 3. 21. 10:12
반응형

스토리 보드 로그인 화면 모범 사례, 로그 아웃시 데이터 지우기 처리


스토리 보드를 사용하여 iOS 앱을 만들고 있습니다. 루트 뷰 컨트롤러는 탭 막대 컨트롤러입니다. 로그인 / 로그 아웃 프로세스를 만들고 있는데 대부분 제대로 작동하지만 몇 가지 문제가 있습니다. 이 모든 것을 설정하는 가장 좋은 방법을 알아야합니다.

다음을 달성하고 싶습니다.

  1. 앱을 처음 시작할 때 로그인 화면을 표시하십시오. 로그인 할 때 Tab Bar Controller의 첫 번째 탭으로 이동하십시오.
  2. 이후 앱을 시작할 때마다 로그인했는지 확인하고 루트 Tab Bar Controller의 첫 번째 탭으로 바로 건너 뜁니다.
  3. 로그 아웃 버튼을 수동으로 클릭하면 로그인 화면이 표시되고 뷰 컨트롤러에서 모든 데이터가 지워집니다.

내가 지금까지 한 일은 루트보기 컨트롤러를 탭 막대 컨트롤러로 설정하고 내 로그인보기 컨트롤러에 대한 사용자 정의 segue를 만들었습니다. 내 Tab Bar Controller 클래스 내에서 viewDidAppear메소드 내부에 로그인했는지 확인 하고 segue를 수행합니다.[self performSegueWithIdentifier:@"pushLogin" sender:self];

또한 로그 아웃 작업을 수행해야하는시기에 대한 알림을 설정했습니다. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

로그 아웃 할 때 키 체인에서 자격 증명을 지우고 [self setSelectedIndex:0]segue를 실행 하고 수행하여 로그인 뷰 컨트롤러를 다시 표시합니다.

이 모든 것이 잘 작동하지만 궁금합니다. 이 논리가 AppDelegate에 있어야합니까? 또한 두 가지 문제가 있습니다.

  • 앱을 처음 시작할 때 segue가 수행되기 전에 Tab Bar Controller가 잠깐 표시됩니다. 코드를 viewWillAppear옮기려고했지만 segue가 일찍 작동하지 않습니다.
  • 로그 아웃해도 모든 데이터는 여전히 모든 뷰 컨트롤러 내에 있습니다. 새 계정으로 로그인하면 이전 계정 데이터는 새로 고칠 때까지 계속 표시됩니다. 로그 아웃시 쉽게 지우는 방법이 필요합니다.

이 작업을 다시 시작할 수 있습니다. 로그인 화면을 루트보기 컨트롤러로 만들거나 모든 것을 처리하기 위해 AppDelegate에서 탐색 컨트롤러를 만드는 것을 고려했습니다 ...이 시점에서 가장 좋은 방법이 무엇인지 잘 모르겠습니다.


스토리 보드는 다음과 같아야합니다

didFinishLaunchingWithOptions 안의 appDelegate.m에서

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

}

스위프트 4 버전

초기 뷰 컨트롤러가 서명 된 TabbarController라고 가정하고 앱 델리게이트의 didFinishLaunchingWithOptions

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

여기에 내가 모든 것을 성취하기 위해 한 일이 있습니다. 이 외에도 (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];

}

편집 : 로그 아웃 작업을 추가하십시오.

여기에 이미지 설명을 입력하십시오

1. 먼저 앱 위임 파일을 준비하십시오.

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.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 은 추가 도움이 필요한 샘플 프로젝트입니다.


AppDelegate뷰 컨트롤러 내부 를 사용하기 때문에 bhavya의 대답을 좋아하지 않았 으며 설정 rootViewController에는 애니메이션이 없습니다. 그리고 Trevor의 답변은 iOS8에서 깜박이는 뷰 컨트롤러에 문제가 있습니다.

UPD 07/18/2015

View Controller 내부의 AppDelegate :

뷰 컨트롤러 내에서 AppDelegate 상태 (속성)를 변경하면 캡슐화가 중단됩니다.

모든 iOS 프로젝트에서 매우 간단한 객체 계층 구조 :

AppDelegate (소유자 windowrootViewController)

ViewController (소유자 view)

상단의 객체가 객체를 생성하기 때문에 하단의 객체가 변경되는 것이 좋습니다. 그러나 맨 아래에있는 객체가 그 위에 객체를 변경하면 괜찮습니다 (기본 프로그래밍 / OOP 원칙 : DIP (Dependency Inversion Principle : 상위 레벨 모듈은 하위 레벨 모듈에 의존해서는 안되지만 추상화에 의존해야 함)에 대해 설명했습니다) ).

객체가이 계층 구조의 객체를 변경하면 조만간 코드에 혼란이 발생합니다. 작은 프로젝트에서는 괜찮을지도 모르지만 비트 프로젝트 에서이 혼란을 파헤치는 것은 재미 없습니다.

UPD 07/18/2015

UINavigationController(tl; dr : check project )를 사용하여 모달 컨트롤러 애니메이션을 복제합니다 .

UINavigationController내 앱에 모든 컨트롤러를 표시하는 데 사용 하고 있습니다. 처음에는 일반 푸시 / 팝 애니메이션으로 탐색 스택에 로그인 뷰 컨트롤러를 표시했습니다. 나는 최소한의 변화로 모달로 변경하기로 결정했습니다.

작동 방식 :

  1. 초기보기 컨트롤러 (또는 self.window.rootViewController)는 ProgressViewController를 a로 사용하는 UINavigationController입니다 rootViewController. DataModel 이이 기사 와 같은 핵심 데이터 스택에 있기 때문에 초기화하는 데 시간이 걸릴 수 있기 때문에 ProgressViewController를 보여줍니다 (이 방법은 정말 좋습니다).

  2. AppDelegate는 로그인 상태 업데이트를 담당합니다.

  3. DataModel은 사용자 로그인 / 로그 아웃을 처리하고 AppDelegate는 userLoggedInKVO를 통해 해당 속성을 관찰합니다 . 아마도 이것을하는 가장 좋은 방법은 아니지만 그것은 나를 위해 작동합니다. (왜 KVO 나쁜, 당신은에서 확인할 수 있습니다 또는 이 문서 (왜 사용하지 않을 알림? 부분).

  4. ModalDismissAnimator 및 ModalPresentAnimator는 기본 푸시 애니메이션을 사용자 정의하는 데 사용됩니다.

애니메이터 로직 작동 방식 :

  1. AppDelegate는 self.window.rootViewController(UINavigationController) 의 대리자로 설정됩니다 .

  2. AppDelegate는 -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]필요한 경우 애니메이터 중 하나를 반환 합니다.

  3. 애니메이터 구현 -transitionDuration:-animateTransition:방법. -[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]];
         }];
    }
    

테스트 프로젝트는 여기에 있습니다 .


미래의 모든 구경꾼을위한 Swifty 솔루션은 다음과 같습니다.

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를 변경합니다!


앱 델리게이트에서이 작업을 수행하지 않는 것이 좋습니다. AppDelegate는 실행, 일시 중단, 종료 등과 관련된 앱 수명주기를 관리합니다. 의 초기보기 컨트롤러에서이 작업을 수행하는 것이 좋습니다 viewDidAppear. 당신은 할 수 self.presentViewControllerself.dismissViewController로그인 뷰 컨트롤러에서. bool키가 NSUserDefaults처음으로 실행되는지 확인하려면 키를 저장 하십시오.


Xcode 7에서는 스토리 보드가 여러 개있을 수 있습니다. 로그인 흐름을 별도의 스토리 보드에 보관할 수 있으면 더 좋습니다.

SELECT VIEWCONTROLLER> 편집기> 스토리 보드로 리팩터링을 사용하여 수행 할 수 있습니다.

여기에보기를 RootViewContoller-로 설정하는 Swift 버전이 있습니다.

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

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

** LoginViewController ** 및 ** TabBarController **를 작성하십시오.

LoginViewControllerTabBarController를 생성 한 후 StoryboardID를 각각 " loginViewController "및 " tabBarController " 로 추가해야합니다 .

그런 다음 Constant 구조체 를 만드는 것을 선호합니다 .

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

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

에서 LoginViewController 추가 IBAction를을 :

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

에서 ProfileViewController 추가 IBAction를을 :

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

에서 AppDelegate에 코드의 라인을 추가 didFinishLaunchingWithOptions :

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

    Switcher.updateRootViewController()

    return true
}

마지막으로 스위처 클래스를 만듭니다 .

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

    }

}

그게 다야!


이것을 사용하여 첫 번째 발사를 확인합니다.

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

(사용자가 앱을 삭제 한 후 다시 설치하면 첫 번째 실행으로 계산됩니다)

AppDelegate에서 첫 실행을 확인하고 로그인 화면 (로그인 및 등록)으로 탐색 컨트롤러를 만듭니다. 현재 메인 창 위에 놓았습니다.

[self.window makeKeyAndVisible];

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

이것은 일반 뷰 컨트롤러 위에 있기 때문에 나머지 앱과 독립적이므로 더 이상 필요하지 않으면 뷰 컨트롤러를 닫을 수 있습니다. 사용자가 버튼을 수동으로 누르면 뷰를 이런 식으로 표시 할 수도 있습니다.

BTW : 사용자의 로그인 데이터를 다음과 같이 저장합니다.

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

로그 아웃 : CoreData에서 너무 느리게 전환했으며 NSArrays 및 NSDictionaries를 사용하여 데이터를 관리합니다. 로그 아웃은 해당 배열과 사전을 비우는 것을 의미합니다. 또한 viewWillAppear에서 데이터를 설정해야합니다.

그게 다야.


나는 당신과 같은 상황에 있으며 데이터 정리를 위해 찾은 솔루션은 뷰 컨트롤러가 정보를 얻기 위해 의존하는 모든 CoreData 항목을 삭제하는 것입니다. 그러나 나는 여전히이 접근법이 매우 나쁘다는 것을 알았습니다. 스토리 보드없이 코드를 사용하여 뷰 컨트롤러 간의 전환을 관리하는보다 우아한 방법이 가능하다고 생각합니다.

코드로만 모든 것을 수행하는 Github 에서이 프로젝트찾았 으며 이해하기 쉽습니다. 그들은 페이스 북과 같은 사이드 메뉴를 사용하며 사용자가 로그인했는지 여부에 따라 센터 뷰 컨트롤러를 변경합니다. 사용자가 로그 아웃하면 appDelegateCoreData에서 데이터 제거되고 기본보기 컨트롤러가 다시 로그인 화면으로 설정됩니다.


감사 bhavya의 솔루션. 신속한에 대한 두 가지 답변이 있었지만 그다지 손상되지 않았습니다. 나는 swift3에서 그렇게했습니다. 아래는 주요 코드입니다.

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
}

SignUpViewController.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")
}

logOutAction 함수에서

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

앱에서 해결해야 할 비슷한 문제가 있었고 다음 방법을 사용했습니다. 탐색 처리에 알림을 사용하지 않았습니다.

앱에 스토리 보드가 3 개 있습니다.

  1. 스플래시 화면 스토리 보드-앱 초기화 및 사용자가 이미 로그인했는지 확인
  2. 로그인 스토리 보드-사용자 로그인 흐름 처리
  3. 탭 바 스토리 보드-앱 컨텐츠 표시

앱의 초기 스토리 보드는 스플래시 화면 스토리 보드입니다. 뷰 컨트롤러 탐색을 처리하기 위해 로그인 및 탭 막대 스토리 보드의 루트로 탐색 컨트롤러가 있습니다.

앱 탐색을 처리하기 위해 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 스토리 보드를 제거하고 로그인 스토리 보드로 바꿉니다.

로그인 스토리 보드에서 사용자가 성공적으로 로그인하면 사용자 데이터를 사용자 기본값에 저장하고 UserData 싱글 톤을 초기화하여 사용자 세부 정보에 액세스합니다. 그런 다음 네비게이터 방법을 사용하여 탭 막대 스토리 보드가로드됩니다.

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 싱글 톤을 초기화하고 로그인 화면 대신 탭 막대를 표시합니다.


여기에 이미지 설명을 입력하십시오

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;

}

view controller.m 보기에서로드되었습니다

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

참고 URL : https://stackoverflow.com/questions/19962276/best-practices-for-storyboard-login-screen-handling-clearing-of-data-upon-logou

반응형