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

292
Trevor Gehman 2013-11-14 09:07.

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

私は以下を達成したい:

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

これまでに行ったことは、ルートビューコントローラーをタブバーコントローラーに設定し、ログインビューコントローラーへのカスタムセグエを作成することです。Tab Bar Controllerクラス内で、viewDidAppearメソッド内にログインしているかどうかを確認し、セグエを実行します。[self performSegueWithIdentifier:@"pushLogin" sender:self];

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

ログアウトしたら、キーチェーンから資格情報をクリアし、[self setSelectedIndex:0]実行してセグエを実行し、ログインビューコントローラーを再度表示します。

これはすべて正常に機能しますが、疑問に思っています。このロジックはAppDelegateに含める必要がありますか?また、2つの問題があります。

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

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

12 answers

313
bhavya kothari 2014-02-19 20:45.

あなたの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;

}

Swift4バージョン

最初のViewControllerがサインインされた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
97
Trevor Gehman 2014-02-20 08:27.

これが私がすべてを達成するためにやったことです。これに加えて考慮する必要があるのは、(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];

}
20
Dimitris Bouzikas 2014-02-20 00:18.

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

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は、追加のヘルプのためのサンプルプロジェクトです。

16
derpoliuk 2015-06-06 01:03.

AppDelegateView Controllerの内部を使用しrootViewControllerていて、設定にアニメーションがないため、bhavyaの回答が気に入らなかった。そして、Trevorの答えは、iOS8でのViewControllerのフラッシュに問題があります。

UPD 2015年7月18日

View Controller内のAppDelegate:

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

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

AppDelegate(所有windowおよびrootViewController

ViewController(所有view

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

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

UPD 2015年7月18日

UINavigationController(tl; dr:プロジェクトを確認してください)を使用してモーダルコントローラーアニメーションを複製します。

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

使い方:

  1. 初期ビューコントローラ(またはself.window.rootViewController)は、ProgressViewControllerをとして持つUINavigationControllerrootViewControllerです。この記事のようにコアデータスタックを初期化するため、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:]、必要に応じてアニメーターの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]];
         }];
    }
    

テストプロジェクトはこちらです。

11
Harry Bloom 2017-05-19 06:44.

これが将来の見物人のための私の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を変更します。

8
Mihado 2015-12-10 12:14.

アプリデリゲートからこれを行うことはお勧めしません。AppDelegateは、起動、一時停止、終了などに関連するアプリのライフサイクルを管理します。の最初のViewControllerからこれを行うことをお勧めしますviewDidAppear。次のことが可能self.presentViewControllerself.dismissViewControllerログインビューコントローラから。保管bool中の鍵NSUserDefaults、それは最初に起動しないかどうかを確認します。

6
iAleksandr 2018-07-09 14:07.

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
}

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

    }

}

以上です!

5
Mahbub Morshed 2016-03-08 01:47.

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

これは、SELECT VIEWCONTROLLER> Editor> Refactor toStoryboardを使用して実行できます。

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

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

    let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController")
3
Thorsten 2014-02-19 19:19.

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

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

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

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

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

ログアウトの場合:CoreDataから切り替えて(遅すぎる)、NSArrayとNSDictionariesを使用してデータを管理しています。ログアウトとは、それらの配列と辞書を空にすることを意味します。さらに、viewWillAppearにデータを設定してください。

それでおしまい。

0
amb 2014-01-09 04:36.

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

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

0
Jithin 2018-05-25 21:11.

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

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

0
salanswift 2020-08-16 06:51.

Xcode 11の@iAleksandrの回答を更新するには、シーンキットが原因で問題が発生します。

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

ガードレットwindowScene = UIApplication.shared.connectedScenes.first as?UIWindowScene、let sceneDelegate = windowScene.delegate as?SceneDelegate else {return
} sceneDelegate.window?.rootViewController = rootViewController

  1. 次のように、アプリデリゲートではなくシーンデリゲートでSwitcher.updateRootViewcontrollerを呼び出します。

func scene(_シーン:UIScene、willConnectToセッション:UISceneSession、オプションconnectionOptions:UIScene.ConnectionOptions){

Switcher.updateRootViewController()

ガードlet_ =(scene as?UIWindowScene)else {return}
}

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

引退した一郎:「私はただ死ぬと思う」

引退した一郎:「私はただ死ぬと思う」

写真:リッチシュルツ/ APマーリンズ外野手/野球のトゥルーヒットキング鈴木一郎は43歳ですが、近い将来、プロ野球をやめるつもりはありません。昨年、彼は143試合に出場し、ヒットしました。

12年後、有名なクラブペンギン氷山がついにヒント

12年後、有名なクラブペンギン氷山がついにヒント

約12年間の運用後、ディズニーの子供向けのMMOクラブペンギンは閉店しますが、プレーヤーがゲームで最も長く実行されているミームの1つを実現する機会を得る前ではありません。氷山がひっくり返った。

安い航空券はあなたが思っているほどお得ではありません

安い航空券はあなたが思っているほどお得ではありません

お気に入りのレストランが割引価格で食事を提供し始めたが、ナプキン、フォーク、または座席が必要な場合は追加料金を支払う必要があるとします。私たちのほとんどはおそらく彼らに私たちのビジネスを与えることを躊躇するでしょうが、その躊躇は航空運賃に変換されません。

注意してください、あなたが話す次の男はUFCファイターかもしれません

注意してください、あなたが話す次の男はUFCファイターかもしれません

マークハントはUFCで最もヘビーな打者かもしれません。彼はほぼ間違いなく、現在オーストラリアに住んでいる最もタフなファイターです。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

安全な公衆パニックルーム

安全な公衆パニックルーム

不気味な音に慣れて、コンクリートの周りをうろつきます。痛みや苦い口を吐き出し、「過ち」の不思議な病気を通してもう一度味わうことを切望します。

長い間行方不明だった 2 つのサンフランシスコ クリークが間もなく日の目を見る可能性がある

都市の水路を回復する理由はたくさんありますが、気候が変化するにつれて、それらはすべてより持続可能な未来につながります.

長い間行方不明だった 2 つのサンフランシスコ クリークが間もなく日の目を見る可能性がある

サンフランシスコの長い間失われた小川を復元する動きが高まっており、コミュニティを地域の生態系と結びつけ、また、都市が気候変動や季節的な洪水に適応するのに役立つ新しいタイプの「グリーン インフラストラクチャ」を作成しています。最新の取り組みは町の 2 つの非常に異なる場所で行われており、それらは明確ではあるが補完的な方法で展開されています。

サンフランシスコのベイエリアで家族写真が撮れる場所トップ 5

サンフランシスコのベイエリアで家族写真が撮れる場所トップ 5

ベイエリアには写真を撮るための美しい屋外スポットがたくさんあります。この記事のすべての写真は、ベイエリアにいるさまざまな PictureHum フォトグラファーとの PictureHum セッションからのものです。

退屈に耐えられないから生きていけない

退屈に耐えられないから生きていけない

それが現代の衝動です — より少ないもので世界を覆し、私たちのほんのわずかな瞬間を自分たちから盗もうとする. 私はかつてダイニングルームのソファで一日を過ごし、祖父母の農家に斜めに差し込む光を眺め、私には関係のない世界について話している大人のつぶやきを半分聞いたり、壁を通してくぐもったテレビを聞いたり、遊んだりしました。床と犬と私の小さな靴を暖めた太陽の黒点と。

Language