programing

Storyboard 로그인 화면, 로그아웃 시 데이터 삭제 처리의 베스트 프랙티스

telebox 2023. 4. 22. 09:17
반응형

Storyboard 로그인 화면, 로그아웃 시 데이터 삭제 처리의 베스트 프랙티스

스토리보드를 사용하여 iOS 앱을 만들고 있습니다.루트 뷰 컨트롤러는 탭 바 컨트롤러입니다.로그인/로그아웃 프로세스를 만들고 있는데 대부분 정상적으로 동작하고 있습니다만, 몇 가지 문제가 있습니다.이 모든 것을 설정하는 최선의 방법을 알고 싶습니다.

나는 다음을 달성하고 싶다.

  1. 앱을 처음 시작할 때 로그인 화면을 보여줍니다.로그인하면 탭바 컨트롤러의 첫 번째 탭으로 이동합니다.
  2. 그 후 앱을 실행할 때마다 로그인 여부를 확인하고 루트 탭 바 컨트롤러의 첫 번째 탭으로 바로 건너뜁니다.
  3. 로그아웃 버튼을 수동으로 클릭하여 로그인 화면을 표시하고 뷰 컨트롤러에서 모든 데이터를 지웁니다.

지금까지 루트 뷰 컨트롤러를 탭바 컨트롤러로 설정하고 로그인 뷰 컨트롤러에 커스텀세그를 작성했습니다.클래스에서 합니다.viewDidAppear a 를 실행한다: segue: segue.[self performSegueWithIdentifier:@"pushLogin" sender:self];

을 실행할가 있는 합니다.「 」[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

시 키 '키 체인'을 실행합니다.[self setSelectedIndex:0]segue를 실행하여 로그인 뷰 컨트롤러를 다시 표시합니다.

이 모든 것은 잘 작동하지만, 이 논리가 AppDelegate에 있어야 하는지 궁금합니다.또, 다음의 2개의 문제가 있습니다.

  • 처음 앱을 시작할 탭 바 컨트롤러는 segue를 수행하기 전에 잠깐 표시됩니다.코드 이동을 시도했습니다.viewWillAppear하지만 세그는 그렇게 빨리 작동되지 않을 겁니다
  • 로그아웃 시 모든 데이터는 여전히 모든 뷰 컨트롤러 안에 있습니다.새 계정에 로그인해도 새로 고칠 때까지 이전 계정 데이터가 계속 표시됩니다.로그아웃 시 쉽게 클리어할 수 있는 방법이 필요합니다.

나는 이것을 다시 할 용의가 있다.로그인 화면을 루트 뷰 컨트롤러로 하거나 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];
}

MyTabTreeViewController.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는 초기 뷰 컨트롤러가 TabbarController에 서명되어 있다고 가정합니다.

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

MyTab ThreeView컨트롤러

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

}

EDIT: 로그아웃 액션을 추가합니다.

여기에 이미지 설명 입력

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 "initial View"도 추가합니다.

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. 새로운 컨트롤러 Login View Controller를 생성하여 로그인 뷰에 연결합니다.

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를 추가하여 탭 ViewController의 프로파일뷰에 연결합니다.

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 뷰 및 rootViewController애니메이션이 없습니다.그리고 Trevor의 답변은 iOS8의 뷰 컨트롤러의 점멸에 문제가 있습니다.

2015년 7월 18일 갱신

AppDelegate View 컨트롤러 내부:

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

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

AppDelegate(유) 소유)window ★★★★★★★★★★★★★★★★★」rootViewController)

View Controller(소유)view)

맨 위의 개체가 맨 아래에서 개체를 생성하기 때문에 개체를 변경할 수 있습니다.그러나 하단의 객체가 그 위에서 객체를 변경하는 것은 문제가 되지 않습니다(기본 프로그래밍/OOP 원리: DIP(Dependency Inversion Principle: 상위 레벨 모듈은 하위 레벨 모듈에 의존하지 않고 추상화에 의존해야 합니다).

오브젝트가 이 계층의 오브젝트를 변경하면 조만간 코드에 혼란이 생깁니다.작은 프로젝트라면 괜찮겠지만 비트프로젝트라면 이 난맥상을 헤쳐나가는 건 재미없어 =]

2015년 7월 18일 갱신

을 복제합니다.UINavigationController(tl;dr: 프로젝트 확인).

하고 UINavigationController모든 컨트롤러를 앱에 표시할 수 있습니다.처음에 로그인 뷰 컨트롤러를 일반 푸시/팝 애니메이션으로 내비게이션 스택에 표시했습니다.그래서 최소한의 변경으로 모달로 바꾸기로 했습니다.

구조:

  1. 뷰 " " " )self.window.rootViewController 사용하는 입니다.「 ProgressViewController 」「 UINavigationController 」입니다.rootViewControllerProgressViewController를 표시하고 있는 이유는 DataModel은 이 기사에서와 같이 코어 데이터 스택을 삽입하기 때문에 초기화에 시간이 걸릴 수 있기 때문입니다(이 접근방식은 매우 마음에 듭니다).

  2. AppDelegate는 로그인 상태 업데이트를 가져옵니다.

  3. 은 사용자 Model을하고 있습니다.AppDelegate는 AppDelegate를 감시하고 있습니다.userLoggedInKVO를 통한 재산최적의 방법은 아니지만 나에게는 효과가 있습니다.(KVO가 나쁜 이유는 이 기사 또는기사(알림 사용 안 함)에서 확인할 수 있습니다.부분)을 클릭합니다.

  4. 모달디스미스Animator 및 ModalPresentAnimator는 기본 푸시 애니메이션을 사용자 지정하는 데 사용됩니다.

애니메이터 논리 구조:

  1. 을 AppDelegate의 합니다.self.window.rootViewController(UINavigation Controller).

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

테스트 프로젝트는 이쪽입니다.

미래의 구경꾼을 위한 스위프티 솔루션을 소개합니다.

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)하여 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
    }

}

여기서부터 내 프로토콜 확장이 로직을 처리하거나 사용자가 로그인/로그아웃했는지 여부를 판단하고 그에 따라 윈도우 rootViewController를 변경합니다.

앱 위임자로부터 이 작업을 수행하는 것은 권장되지 않습니다.AppDelegate는 시작, 일시정지, 종료 등과 관련된 앱의 라이프 사이클을 관리합니다.은, 「 」, 「 」의뷰 컨트롤러에서 실시하는 것을 합니다.viewDidAppear 하면 됩니다.self.presentViewController ★★★★★★★★★★★★★★★★★」self.dismissViewController이치노「」의 bool를 누르다NSUserDefaults첫 출시 여부를 확인하려고 합니다.

**LoginViewController* 및 **TabBarController*를 만듭니다.

LoginViewControllerTabBarController를 작성한 후 스토리보드를 추가해야 합니다.ID는 각각 "loginViewController" 및 "TabBarController"입니다.

그런 다음 상수 구조를 만드는 것이 좋습니다.

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

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

LoginViewController에서 IBAtion을 추가합니다.

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

ProfileViewController에서 IBAtion 추가:

@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

    }

}

이상입니다!

Xcode 7에서는 여러 storyBoard를 사용할 수 있습니다.로그인 플로우를 다른 스토리보드에 저장할 수 있으면 더 좋습니다.

이것은 SELECT VIEW CONTROLER > 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")

첫 번째 출시를 확인할 때 사용합니다.

- (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(너무 느림)에서 벗어나 NSAray와 NSDiagentary를 사용하여 데이터를 관리하고 있습니다.로그아웃이란 어레이와 사전을 비우는 것을 의미합니다.또한 viewWillAple에서 데이터를 설정합니다.

바로 그겁니다.

Scene Kit로 인해 문제가 발생하는 Xcode 11에 대해 @iAlexandr를 업데이트하려면 다음과 같이 하십시오.

  1. 교체하다
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = rootViewController

와 함께

guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,let sceneDelegate = windowScene.delegate as? SceneDelegate         else {
   return       
}
sceneDelegate.window?.rootViewController = rootViewController
  1. 다음과 같이 앱 위임 대신 장면 위임에서 Switcher.updateRootView컨트롤러를 호출합니다.

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
    {        
    
     Switcher.updateRootViewController()
     guard let _ = (scene as? UIWindowScene) else { return }     
    }
    

저도 당신과 같은 상황입니다.데이터를 정리하기 위해 찾은 솔루션은 뷰 컨트롤러가 정보를 그리기 위해 사용하는 모든 CoreData를 삭제하는 것입니다.그러나 이 접근 방식은 여전히 매우 좋지 않다는 것을 깨달았습니다. 스토리보드 없이 코드만으로 뷰 컨트롤러 간의 전환을 관리할 수 있는 보다 우아한 방법이 실현될 수 있다고 생각합니다.

Github에서 이 모든 것을 코드로만 하는 프로젝트를 찾았는데 이해하기 쉬워요.이들은 페이스북과 같은 사이드 메뉴를 사용하며, 사용자가 로그인했는지 여부에 따라 중앙 뷰 컨트롤러를 변경합니다.사용자가 로그아웃 했을 때appDelegate는 CoreData에서 데이터를 삭제하고 메인뷰 컨트롤러를 다시 로그인 화면으로 설정합니다.

앱에서 해결해야 할 비슷한 문제가 있어서 다음과 같은 방법을 사용했습니다.나는 내비게이션을 다룰 때 알림을 사용하지 않았다.

나는 앱에 스토리보드가 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)

그러면 앱 창의 루트에서 슬래시 스토리보드가 제거되고 로그인 스토리보드로 대체됩니다.

로그인 스토리보드에서 사용자가 정상적으로 로그인하면 사용자 데이터를 User Defaults에 저장하고 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)
  • 사용자가 로그인하면 앱이 강제 종료됩니다.

사용자가 앱을 실행하면 스플래시 화면이 로드됩니다.사용자가 로그인하고 있는지 확인하고 User Defaults에서 사용자 데이터에 액세스합니다.그런 다음 UserData 싱글톤을 초기화하여 로그인 화면 대신 탭 바를 표시합니다.

bhavya의 솔루션에 감사드립니다.Swift에 대한 두 가지 답변이 있었지만, 그것들은 매우 온전하지는 않습니다.나는 그것을 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")
}

in logOutAction 함수

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

여기에 이미지 설명 입력

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;

}

뷰 컨트롤러.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

반응형