ios - 署名 - ストーリーボードログイン画面のベストプラクティス、ログアウト時のデータのクリア処理




swift フレーム ワーク (9)

Bhavyaのソリューションに感謝します。すぐに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()
}

私はストーリーボードを使ってiOSアプリを構築しています。 ルートビューコントローラはタブバーコントローラです。 私はログイン/ログアウトプロセスを作成していますが、ほとんどの場合うまくいきますが、いくつかの問題があります。 私はこれをすべて設定する最良の方法を知る必要があります。

私は以下を達成したい:

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

私がこれまで行ってきたことは、タブバーコントローラにルートビューコントローラを設定し、ログインビューコントローラにカスタムセグを作成しました。 私の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にあるべきですか? 私にも2つの問題があります:

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

私はこれを再加工するために開いています。 私は、ログイン画面をルートビューコントローラにするか、またはAppDelegateでナビゲーションコントローラを作成してすべてを処理することを検討しました...この時点で、最も良い方法が何であるか分かりません。


View Controller内でAppDelegateを使用し、 AppDelegateにアニメーションが設定さrootViewControllerていないため、bhavyaの回答がrootViewController 。 Trevorの答えにはiOS8の点滅するView Controllerに関する問題があります。

UPD 07/18/2015

View Controller内のAppDelegate:

ビューコントローラ内のAppDelegate状態(プロパティ)を変更すると、カプセル化が解除されます。

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

AppDelegate( windowrootViewController所有)

ViewController(所有view

上から見たオブジェクトは下にあるオブジェクトを変更しているので、オブジェクトを下から変更しても問題ありません。 しかし、ボトムのオブジェクトがそれらの上にオブジェクトを変更しても問題ありません(私は基本的なプログラミング/ OOPの原則について記述しています:DIP(依存性反転原理:高レベルモジュールは低レベルモジュールに依存してはいけませんが、抽象化に依存する必要があります) )。

どのオブジェクトでもこの階層のオブジェクトが変更されると、遅かれ早かれコード内に混乱が生じます。 それは小さなプロジェクトでは大丈夫かもしれませんが、ビットプロジェクトでこの混乱を掘り起こすのは楽しいことではありません=]

UPD 07/18/2015

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

私はUINavigationControllerを使用して、すべてのコントローラをアプリケーションに表示しています。 最初は、単純なプッシュ/ポップアニメーションでナビゲーションスタックにログインビューコントローラを表示しました。 最小限の変更でモーダルに変更することにしました。

使い方:

  1. 初期ビューコントローラ(またはself.window.rootViewController )は、ProgressViewControllerをrootViewControllerとして持つrootViewControllerです。 私はProgressViewControllerを表示しています。なぜなら、DataModelはこのarticleようなコアデータスタックをインクリメントするために初期化するのに時間がかかるからです(私はこのアプローチが本当に好きです)。

  2. AppDelegateは、ログインステータスの更新を取得します。

  3. DataModelはユーザログイン/ログアウトを処理し、AppDelegateはKVOを介してuserLoggedInプロパティをuserLoggedInます。 間違いなくこれを行うための最良の方法ではないが、それは私のために働く。 (なぜKVOが悪いのか、 この記事この記事をチェックインすることができます(なぜNot Not Notificationsを使うのか)。

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

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

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

  2. AppDelegateは、必要に応じて-[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]のアニメータの1つを返します。

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

テストプロジェクトはproject


ここに画像の説明を入力

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)
{
    //[email protected]"homeViewControllerId";
    UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window];
    UITabBarController *tabBarVC =
    [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"];
    mainWindow.rootViewController=tabBarVC;
}
else
{


    [email protected]"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 viewでロードしました

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

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

AppDelegatedidFinishLaunchingWithOptionsのコード行を追加します

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

    Switcher.updateRootViewController()

    return true
}

最後に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

    }

}

それだけです!


ここで私はすべてを達成するためにやったことです。 これに加えて考慮する必要があるのは、(a)ログインプロセスと(b)アプリケーションデータを格納する場所(この場合はシングルトンを使用しました)だけです。

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

AppDelegate.m

このファイルでは、ユーザーが既にログインしているかどうかを確認します。ログインしていない場合は、ログインビューコントローラを押します。 また、データをクリアしてログインビューを表示するログアウトプロセスも処理します。

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

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

    return YES;
}

-(void) showLoginScreen:(BOOL)animated
{

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

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

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

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

}

LoginViewController.m

ここで、ログインが成功すると、私は単にビューを閉じて通知を送信します。

-(void) loginWasSuccessful
{

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

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

}

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


私はあなたと同じ状況にあります。データを消去するために見つけた解決策は、ビューコントローラがその情報を描画するために必要とするすべてのCoreDataを削除することです。 しかし、私はまだこのアプローチが非常に悪いと感じました。私は、ストーリーボードなしで、そしてビューコントローラ間の遷移を管理するコードだけを使用して、これを行うためのよりエレガントな方法を実現できると考えています。

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


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

私はアプリに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シングルトンを初期化してユーザーの詳細にアクセスします。 次に、ナビゲータメソッドを使用して、Tab Bar Storyboardがロードされます。

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


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






clear