web-dev-qa-db-fra.com

Meilleures pratiques pour l'écran de connexion Storyboard, gestion de la suppression des données lors de la déconnexion

Je construis une application iOS en utilisant un Storyboard. Le contrôleur de vue racine est un contrôleur de barre d’onglet. Je crée le processus de connexion/déconnexion, et cela fonctionne généralement bien, mais j'ai quelques problèmes. J'ai besoin de connaître le MEILLEUR moyen de mettre tout cela en place.

Je veux accomplir les tâches suivantes:

  1. Afficher un écran de connexion lors du premier lancement de l'application. Quand ils se connectent, allez au premier onglet du contrôleur de la barre d'onglets.
  2. Chaque fois qu'ils lancent l'application par la suite, vérifiez s'ils sont connectés et passez directement au premier onglet du contrôleur de la barre d'onglets racine.
  3. Lorsqu'ils cliquent manuellement sur un bouton de déconnexion, affichent l'écran de connexion et effacent toutes les données des contrôleurs de vue.

Ce que j'ai fait jusqu'à présent est de définir le contrôleur de vue racine sur le contrôleur de barre d'onglets et de créer une séquence personnalisée sur mon contrôleur de vue de connexion. À l'intérieur de ma classe de contrôleurs de barre d'onglets, je vérifie s'ils sont connectés à la méthode viewDidAppear et exécutons la séquence: [self performSegueWithIdentifier:@"pushLogin" sender:self];

J'ai également configuré une notification pour le moment où l'action de déconnexion doit être effectuée: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Lors de la déconnexion, j'efface les informations d'identification du trousseau, lancez [self setSelectedIndex:0] et effectuez la séquence pour afficher à nouveau le contrôleur de vue de connexion.

Tout cela fonctionne bien, mais je me demande: cette logique devrait-elle être dans AppDelegate? J'ai aussi deux problèmes:

  • La première fois qu'ils lancent l'application, le contrôleur de la barre d'onglets s'affiche brièvement avant l'exécution de la séquence. J'ai essayé de déplacer le code vers viewWillAppear mais la transition ne fonctionnera pas si tôt.
  • Lorsqu'ils se déconnectent, toutes les données sont toujours dans tous les contrôleurs de vue. S'ils se connectent à un nouveau compte, les anciennes données du compte sont toujours affichées jusqu'à ce qu'ils soient actualisés. J'ai besoin d'un moyen pour effacer cela facilement à la déconnexion.

Je suis ouvert à retravailler ceci. J'ai envisagé de faire de l'écran de connexion le contrôleur de vue racine ou de créer un contrôleur de navigation dans AppDelegate pour tout gérer ... Je ne sais pas exactement quelle est la meilleure méthode à ce stade.

284
Trevor Gehman

Voici ce que j'ai fini par faire pour tout accomplir. La seule chose que vous devez considérer en plus de cela est (a) le processus de connexion et (b) où vous stockez les données de votre application (dans ce cas, j'ai utilisé un singleton).

Storyboard showing login view controller and main tab controller

Comme vous pouvez le constater, le contrôleur de vue racine est mon contrôleur d’onglet principal. C'est ce que j'ai fait car après que l'utilisateur se soit connecté, je souhaite que l'application se lance directement sur le premier onglet. (Cela évite tout "scintillement" où la vue de connexion montre temporairement.)

AppDelegate.m

Dans ce fichier, je vérifie si l'utilisateur est déjà connecté. Sinon, j'appuie sur le contrôleur de vue de connexion. Je gère également le processus de déconnexion, où je nettoie les données et affiche la vue de connexion.

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

Ici, si la connexion est réussie, je rejette simplement la vue et envoie une notification.

-(void) loginWasSuccessful
{

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

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

}
97
Trevor Gehman

Your storyboard should look like this

Dans votre appDelegate.m dans votre didFinishLaunchingWithOptions

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

Dans le fichier SignUpViewController.m

- (IBAction)actionSignup:(id)sender
{
    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}

Dans le fichier 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;

}

Version Swift 4

didFinishLaunchingWithOptions dans le délégué de l'application en supposant que votre contrôleur de vue initial est celui qui a été signé dans TabbarController.

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

    return true

Dans le contrôleur de vue d'inscription:

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

EDIT: Ajoutez une action de déconnexion.

enter image description here

1. Commencez par préparer le fichier de délégué de l'application.

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. Créez une classe nommée 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. Créez un nouveau contrôleur RootViewController et connectez-vous à la première vue, où le bouton de connexion est actif. Ajoutez également un ID Storyboard: "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. Créez un nouveau contrôleur LoginViewController connecté à la vue de connexion.

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. A la fin, ajoutez un nouveau contrôleur ProfileViewController et connecté à la vue du profil dans 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 est un exemple de projet pour obtenir une aide supplémentaire.

20
Dimitris Bouzikas

Je n’ai pas aimé la réponse de bhavya à cause de l’utilisation de AppDelegate dans les contrôleurs de vue et du réglage de rootViewController n’a pas d’animation. Et la réponse de Trevor a un problème avec le contrôleur de vue clignotant sur iOS8.

UPD 18/07/2015

AppDelegate inside View Controllers:

La modification de l'état AppDelegate (propriétés) dans le contrôleur de vue interrompt l'encapsulation.

Hiérarchie très simple d'objets dans chaque projet iOS:

AppDelegate (possède window et rootViewController)

ViewController (possède view)

Il est normal que les objets du haut changent d'objets en bas, parce qu'ils les créent. Mais ce n'est pas acceptable si les objets du bas changent d'objets (j'ai décrit quelques principes de base en programmation/POO: DIP (Dependency Inversion Principle: le module de haut niveau ne doit pas dépendre du module de bas niveau, mais doit dépendre d'abstractions). ).

Si un objet va changer n'importe quel objet dans cette hiérarchie, tôt ou tard, il y aura un désordre dans le code. Ce n'est peut-être pas grave pour les petits projets, mais ce n'est pas amusant de creuser dans ce gâchis sur des projets un peu =]

UPD 18/07/2015

Je réplique les animations de contrôleur modal en utilisant UINavigationController (tl; dr: vérifie le projet ).

J'utilise UINavigationController pour présenter tous les contrôleurs de mon application. Initialement, j'ai affiché le contrôleur de vue de connexion dans la pile de navigation avec une animation simple Push/Pop. Alors j'ai décidé de le changer en modal avec des changements minimes.

Comment ça fonctionne:

  1. Le contrôleur de vue initiale (ou self.window.rootViewController) est UINavigationController avec ProgressViewController en tant que rootViewController. Je montre ProgressViewController parce que DataModel peut prendre un certain temps pour s’initialiser car il se trouve dans sa pile de données principale comme dans ce article (j’aime vraiment cette approche).

  2. AppDelegate est responsable de l'obtention des mises à jour de statut de connexion.

  3. DataModel gère la connexion/déconnexion de l'utilisateur et AppDelegate observe que c'est la propriété userLoggedIn via KVO. Ce n’est peut-être pas la meilleure méthode pour le faire, mais cela fonctionne pour moi. (Pourquoi KVO est mauvais, vous pouvez archiver this ou cet article (Pourquoi ne pas utiliser les notifications?).

  4. ModalDismissAnimator et ModalPresentAnimator permettent de personnaliser l'animation Push par défaut.

Comment fonctionne la logique des animateurs:

  1. AppDelegate se définit comme délégué de self.window.rootViewController (qui est UINavigationController).

  2. AppDelegate renvoie l'un des animateurs dans -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:] si nécessaire.

  3. Les animateurs implémentent les méthodes -transitionDuration: et -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]];
         }];
    }
    

Le projet test est ici .

14
derpoliuk

Voici ma solution Swifty pour tous les futurs spectateurs.

1) Créez un protocole pour gérer les fonctions de connexion et de déconnexion:

protocol LoginFlowHandler {
    func handleLogin(withWindow window: UIWindow?)
    func handleLogout(withWindow window: UIWindow?)
}

2) Étendez ledit protocole et fournissez la fonctionnalité ici pour vous déconnecter:

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) Ensuite, je peux conformer mon AppDelegate au protocole LoginFlowHandler et appeler handleLogin au démarrage:

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
    }

}

À partir de là, mon extension de protocole gérera la logique ou déterminera si l'utilisateur est connecté ou non, puis changera le windows rootViewController en conséquence!

10
Harry Bloom

Faire cela depuis le délégué de l'application n'est PAS recommandé. AppDelegate gère le cycle de vie de l'application en ce qui concerne le lancement, la suspension, la terminaison, etc. Je suggère de le faire à partir de votre contrôleur de vue initial dans le viewDidAppear. Vous pouvez self.presentViewController et self.dismissViewController à partir du contrôleur de vue de connexion. Enregistrez une clé bool dans NSUserDefaults pour voir si le lancement a lieu pour la première fois.

8
Mihado

Dans Xcode 7, vous pouvez avoir plusieurs storyBoards. Ce sera mieux si vous pouvez conserver le flux de connexion dans un storyboard séparé.

Cela peut être fait en utilisant SELECT VIEWCONTROLLER> Editor> Refactor to Storyboard

Et voici la version Swift permettant de définir une vue en tant que RootViewContoller-

    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**.

Après avoir créé le LoginViewController et TabBarController , nous devons ajouter un StoryboardID en tant que “ loginViewController ” et “ tabBarController ” respectivement.

Ensuite, je préfère créer la structure Constant :

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

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

Dans LoginViewController ajoutez IBAction :

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

Dans ProfileViewController ajoutez IBAction :

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

Dans AppDelegate ajoutez une ligne de code dans didFinishLaunchingWithOptions :

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

    Switcher.updateRootViewController()

    return true
}

Enfin, créez Switcher la classe:

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

    }

}

C'est tout!

4
iAleksandr

J'utilise ceci pour vérifier le premier lancement:

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

(Si l'utilisateur supprime l'application et la réinstalle, cela compte comme un premier lancement)

Dans AppDelegate, je vérifie le premier lancement et crée un contrôleur de navigation avec les écrans de connexion (login et register), que je mets en haut de la fenêtre principale actuelle:

[self.window makeKeyAndVisible];

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

Comme cela se trouve au-dessus du contrôleur de vue régulier, il est indépendant du reste de votre application et vous pouvez simplement le fermer, si vous n'en avez plus besoin. Et vous pouvez également présenter la vue de cette façon, si l'utilisateur appuie sur un bouton manuellement.

BTW: Je sauvegarde les données de connexion de mes utilisateurs comme ceci:

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

Pour la déconnexion: je me suis éloigné de CoreData (trop lent) et j'utilise NSArrays et NSDictionaries pour gérer mes données maintenant. La déconnexion signifie simplement vider ces tableaux et dictionnaires. De plus, je m'assure de définir mes données dans viewWillAppear.

C'est ça.

3
Thorsten

J'ai eu un problème similaire à résoudre dans une application et j'ai utilisé la méthode suivante. Je n'ai pas utilisé les notifications pour gérer la navigation.

J'ai trois storyboards dans l'application.

  1. Storyboard Splash Screen - pour l’initialisation de l’application et pour vérifier si l’utilisateur est déjà connecté
  2. Storyboard de connexion - pour gérer le flux de connexion de l'utilisateur
  3. Storyboard de la barre d'onglets - pour afficher le contenu de l'application

Mon story-board initial dans l'application est un story-board avec écran Splash. J'ai le contrôleur de navigation comme racine du login et de la barre d'onglets du storyboard pour gérer la navigation du contrôleur de vue.

J'ai créé une classe Navigator pour gérer la navigation de l'application et elle se présente comme suit:

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)

           }

       }

   }

}

Regardons les scénarios possibles:

  • Premier lancement de l'application; L'écran de démarrage sera chargé à l'endroit où je vérifie si l'utilisateur est déjà connecté. L'écran de connexion sera chargé à l'aide de la classe Navigator comme suit:

Depuis que j'ai le contrôleur de navigation comme racine, j'instancie le contrôleur de navigation comme contrôleur de vue initial.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Cela supprime le storyboard slpash de la racine de la fenêtre de l'application et le remplace par le storyboard de connexion.

À partir du storyboard de connexion, lorsque l'utilisateur est connecté avec succès, j'enregistre les données utilisateur dans les paramètres utilisateur par défaut et initialise un singleton UserData pour accéder aux détails de l'utilisateur. Ensuite, le storyboard de la barre d'onglets est chargé à l'aide de la méthode du navigateur.

Let tabBarSB = UIStoryboard(name: "tabBar", bundle: nil)
let tabBarNav = tabBarSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(tabBarNav, from: self)

Maintenant, l'utilisateur se déconnecte de l'écran des paramètres dans la barre d'onglets. J'efface toutes les données utilisateur enregistrées et navigue vers l'écran de connexion.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • L'utilisateur est connecté et tue l'application de force

Lorsque l'utilisateur lance l'application, l'écran de démarrage sera chargé. Je vérifie si l’utilisateur est connecté et accède aux données de l’utilisateur par défaut. Puis initialisez le singleton UserData et affiche la barre d’onglet au lieu de l’écran de connexion.

0
Jithin

Merci la solution de bhavya. Il y a eu deux réponses à propos de Swift, mais celles-ci ne sont pas très intactes. Je fais cela dans le Swift3.Below est le code principal.

Dans 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
}

Dans 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")
}

Dans la fonction 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

Je suis dans la même situation que vous et la solution que j'ai trouvée pour nettoyer les données consiste à supprimer tous les éléments CoreData sur lesquels mes contrôleurs de vision s'appuient pour en tirer des informations. Mais j’ai quand même trouvé cette approche très mauvaise, je pense qu’une manière plus élégante de le faire peut être accomplie sans storyboard et en utilisant uniquement du code pour gérer les transitions entre les contrôleurs de vue.

J'ai trouvé ce projet chez Github qui ne fait tout cela que par code et qui est assez facile à comprendre. Ils utilisent un menu latéral de type Facebook et modifient le contrôleur de vue central en fonction de la connexion ou non de l'utilisateur. Lorsque l'utilisateur se déconnecte, appDelegate supprime les données de CoreData et définit à nouveau le contrôleur d'affichage principal sur l'écran de connexion.

0
amb