web-dev-qa-db-ja.com

ログアウト時のデータ消去を処理するストーリーボードログイン画面のベストプラクティス

ストーリーボードを使ってiOSアプリを作成しています。ルートView ControllerはTab Bar Controllerです。私はログイン/ログアウトプロセスを作成しています、そしてそれは大抵うまく働いています、しかし私はいくつかの問題を持っています。これらすべてを設定するための最良の方法を知る必要があります。

次のことを達成したい。

  1. アプリを最初に起動したときにログイン画面を表示します。ログインしたら、Tab Bar Controllerの最初のタブに移動します。
  2. その後アプリを起動するたびに、ログインしているかどうかを確認し、ルートTab Bar Controllerの最初のタブに直接スキップします。
  3. 手動でログアウトボタンをクリックすると、ログイン画面が表示され、View Controllerからすべてのデータが消去されます。

これまでに行ったことは、ルートView ControllerをTab Bar Controllerに設定し、自分のLogin View Controllerにカスタムセグエを作成することです。私のTab Bar Controllerクラスの中で、それらがviewDidAppearメソッドの中でログインしているかどうかをチェックし、そしてセグエを実行します:[self performSegueWithIdentifier:@"pushLogin" sender:self];

また、ログアウトアクションを実行する必要があるときの通知も設定しました。[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

ログアウトしたら、キーチェーンから認証情報を消去し、[self setSelectedIndex:0]を実行し、セグエを実行してログインView Controllerを再度表示します。

これはすべてうまくいきますが、私は思っています:このロジックはAppDelegateにあるべきですか?私には2つの問題があります。

  • 最初にアプリを起動したとき、タブバーコントローラはセグエが実行される前に少しの間表示されます。私はコードをviewWillAppearに移動しようとしましたが、そのセグエはそれほど早くは機能しません。
  • ログアウトしても、すべてのデータはまだすべてのView Controller内にあります。新しいアカウントにログインしても、古いアカウントのデータは更新されるまで表示されます。 ログアウト時にこれを簡単にクリアする方法が必要です。

私はこれをやり直すことにオープンです。ログイン画面をルートView Controllerにするか、AppDelegateでナビゲーションコントローラを作成してすべてを処理することを検討しました。現時点で最良の方法が何であるかはわかりません。

282
Trevor Gehman

これが私がすべてを達成するためにやったことです。あなたがこれに加えて考慮する必要がある唯一のものは(a)ログインプロセスと(b)あなたがあなたのアプリデータを保存しているところです(この場合、私はシングルトンを使いました)。

Storyboard showing login view controller and main tab controller

ご覧のとおり、ルートView Controllerはmyメインタブコントローラです。これは、ユーザーがログインした後、最初のタブに直接アプリを起動させたいためです。 (これにより、ログインビューに一時的に表示される「ちらつき」を回避できます。)

AppDelegate.m

このファイルでは、ユーザがすでにログインしているかどうかを確認します。ログインしていない場合は、ログインView Controllerをプッシュします。ログアウトプロセスも処理します。データを消去してログインビューを表示します。

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

}
96
Trevor Gehman

Your storyboard should look like this

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;

}

Swift 4バージョン

アプリのデリゲートのdidFinishLaunchingWithOptionsは、最初のView ControllerがサインインしたTabbarControllerであると想定しています。

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

    return true

サインアップView Controller:

@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
305
bhavya kothari

編集:ログアウトアクションを追加します。

enter image description here

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という名前のクラスを作成します。

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 は、追加の支援を受けるためのサンプルプロジェクトです。

20

ビューコントローラの内部でAppDelegateを使用し、rootViewControllerを設定してもアニメーションが表示されないため、bhavyaの回答は嫌いでした。そしてTrevorの答えはiOS8上の点滅するView Controllerに関する問題を抱えている。

UPD 2015/07/18

ビューコントローラ内のAppDelegate:

View Controller内でAppDelegateの状態(プロパティ)を変更すると、カプセル化が解除されます。

すべてのiOSプロジェクトにおける非常に単純なオブジェクトの階層

AppDelegate(windowrootViewControllerを所有)

ViewController(viewを所有)

一番上のオブジェクトが一番下のオブジェクトを変更しても構いません。しかし、一番下のオブジェクトが一番上のオブジェクトを変更しても構いません(基本的なプログラミング/ OOPの原則:DIP(Dependency Inversion Principle:上位モジュールは下位モジュールに依存してはいけませんが抽象化に依存します))。 ).

いずれかのオブジェクトがこの階層内のいずれかのオブジェクトを変更する場合、遅かれ早かれコードに混乱が生じるでしょう。小規模なプロジェクトでは大丈夫かもしれませんが、少し混乱したプロジェクトでこの混乱を掘り下げるのは楽しいことではありません=]

UPD 2015/07/18

UINavigationController(tl; dr: project をチェック)を使ってモーダルコントローラーのアニメーションを複製します。

私は自分のアプリのすべてのコントローラを表示するためにUINavigationControllerを使っています。最初、私は普通のPush/popアニメーションでナビゲーションスタックにログインView Controllerを表示しました。私はそれを最小限の変更でモーダルに変更することにしました。

使い方:

  1. 初期のView Controller(またはself.window.rootViewController)は、ProgressViewControllerをrootViewControllerとしたUINavigationControllerです。私はProgressViewControllerを見せています。なぜならDataModelはこの article のようにコアデータスタックに入るので初期化に時間がかかることがあるからです(私は本当にこのアプローチが好きです)。

  2. AppDelegateは、ログインステータスの更新を取得する責任があります。

  3. DataModelはユーザーのログイン/ログアウトを処理し、AppDelegateはKVOを介してuserLoggedInプロパティを監視しています。おそらくこれを行うための最善の方法ではありませんが、それは私のために働く。 (なぜKVOが悪いのか、 this または this article をチェックインすることができます。part) 。

  4. ModalDismissAnimatorとModalPresentAnimatorは、デフォルトのプッシュアニメーションをカスタマイズするために使用されます。

アニメーターロジックの仕組み:

  1. AppDelegateは、自身をself.window.rootViewController(UINavigationController)のデリゲートとして設定します。

  2. 必要に応じて、AppDelegateはアニメーターの1つを-[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]];
         }];
    }
    

テストプロジェクトは here です。

14
derpoliuk

これは将来の見物人のための私の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
    }

}

ここから、私のプロトコル拡張はロジックを処理するか、あるいはログイン/ログアウトしているかどうかをユーザーが判断し、それに応じてウィンドウのrootViewControllerを変更します。

10
Harry Bloom

アプリデリゲートからこれを行うことはお勧めできません。 AppDelegateは、起動、一時停止、終了などに関連するアプリのライフサイクルを管理します。 viewDidAppearの最初のView Controllerからこれを行うことをお勧めします。ログインビューコントローラからself.presentViewControllerおよびself.dismissViewControllerを実行できます。初めて起動するかどうかを確認するには、boolNSUserDefaultsキーを格納します。

8
Mihado

Xcode 7では、複数のstoryBoardsを持つことができます。ログインフローを別のストーリーボードに保存できればもっと良いでしょう。

これを使用して行うことができます ビューコントローラの選択>エディタ>ストーリーボードへのリファクタリング

そしてこれは、RootViewContollerとしてビューを設定するためのSwiftバージョンです。

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

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

Create **LoginViewController** and **TabBarController**.

LoginViewController および TabBarController を作成したら、StoryboardIDを“ loginViewController ”および“ tabBarController ”として追加する必要があります。それぞれ。

それから、 Constant structを作成します。

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

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

LoginViewController add IBAction

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

ProfileViewController add 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
}

最後に Switcher classを作成します。

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

    }

}

以上ですべてです!

3
iAleksandr

これを使って最初の起動を確認します。

- (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で最初の起動を確認し、ログイン画面(login and register)でナビゲーションコントローラを作成します。これを現在のメインウィンドウの上に置きます。

[self.window makeKeyAndVisible];

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

これは通常のView Controllerの上にあるため、アプリの他の部分からは独立しています。もう必要ない場合は、View Controllerを閉じることができます。また、ユーザーが手動でボタンを押すと、ビューをこのように表示することもできます。

ところで:私はこのように私のユーザーからのログインデータを保存します:

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に自分のデータを必ず設定してください。

それでおしまい。

3
Thorsten

私はアプリで解決するために同様の問題があり、私は次の方法を使用しました。ナビゲーションを処理するために通知を使用しませんでした。

私はアプリ内に3つの絵コンテを持っています。

  1. スプラッシュスクリーンストーリーボード - アプリの初期化とユーザーがすでにログインしているかどうかの確認
  2. ログインストーリーボード - ユーザーログインフローを処理するため
  3. Tab bar storyboard - アプリのコンテンツを表示します

アプリの私の最初の絵コンテはSplashスクリーン絵コンテです。 View Controllerのナビゲーションを処理するためのログインおよびTab BarストーリーボードのルートとしてNavigation Controllerを持っています。

アプリのナビゲーションを処理する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クラスを使用してログインスクリーンがロードされます。

私はルートとしてNavigation Controllerを持っているので、最初のView ControllerとしてNavigation Controllerをインスタンス化します。

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

これにより、slpashストーリーボードがアプリウィンドウのルートから削除され、ログインストーリーボードに置き換えられます。

ログインストーリーボードから、ユーザーが正常にログインしたら、ユーザーデータをUser Defaultsに保存し、UserDataシングルトンを初期化してユーザーの詳細にアクセスします。次にTab barストーリーボードがナビゲーターメソッドを使ってロードされます。

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シングルトンを初期化し、ログイン画面の代わりにタブバーを表示します。

0
Jithin

Bhavyaの解決に感謝します。Swiftについて2つの答えがありましたが、それらはそれほど損なわれていません。 Swift3.Belowにメインコードがあります。

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

私はあなたと同じ状況にあり、データをきれいにするために私が見つけた解決策は、私のView Controllerがそれを情報を描画するのに頼っているすべてのCoreDataのものを削除することです。しかし、私はまだこのアプローチが非常に悪いと思っていました。ストーリーボードを使わずにコードを使用してView Controller間の遷移を管理することなく、よりエレガントな方法を実現できると思います。

私は このプロジェクト をGithubで見つけました。これはこれらすべてをコードによってのみ行い、理解するのは非常に簡単です。彼らはFacebookのようなサイドメニューを使い、彼らがしていることはユーザーがログインしているかいないかに応じてセンタービューコントローラを変更することです。ユーザがログアウトすると、appDelegateはCoreDataからデータを削除し、メインView Controllerを再びログイン画面に設定します。

0
amb