it-swarm.com.de

Best Practices für den Storyboard-Anmeldebildschirm, der das Löschen von Daten beim Abmelden behandelt

Ich erstelle eine iOS-App mit einem Storyboard. Der Controller für die Stammansicht ist ein Controller für die Registerkartenleiste. Ich erstelle den Anmelde-/Abmeldeprozess und er funktioniert größtenteils einwandfrei, aber ich habe ein paar Probleme. Ich muss den BESTEN Weg kennen, um das alles einzurichten.

Ich möchte folgendes erreichen:

  1. Beim ersten Start der App einen Anmeldebildschirm anzeigen. Wechseln Sie nach der Anmeldung zur ersten Registerkarte des Registerkartenleisten-Controllers.
  2. Überprüfen Sie bei jedem Neustart der App, ob sie angemeldet sind, und springen Sie direkt zur ersten Registerkarte des Registerkartenleisten-Controllers im Stammverzeichnis.
  3. Wenn sie manuell auf eine Abmeldeschaltfläche klicken, zeigen Sie den Anmeldebildschirm an und löschen Sie alle Daten von den View Controllern.

Bisher habe ich den Stammansichts-Controller auf den Registerkartenleisten-Controller eingestellt und einen benutzerdefinierten Bereich für meinen Anmeldeansicht-Controller erstellt. In meiner Tab Bar Controller-Klasse überprüfe ich, ob sie in der viewDidAppear -Methode angemeldet sind, und führe den folgenden Schritt aus: [self performSegueWithIdentifier:@"pushLogin" sender:self];

Ich richte auch eine Benachrichtigung ein, wenn die Abmeldeaktion ausgeführt werden muss: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Nach dem Abmelden lösche ich die Anmeldeinformationen aus dem Schlüsselbund, führe [self setSelectedIndex:0] Aus und führe den Vorgang aus, um den Anmeldeansichtscontroller erneut anzuzeigen.

Das alles funktioniert gut, aber ich frage mich: Sollte diese Logik in der AppDelegate sein? Ich habe auch zwei Probleme:

  • Beim ersten Start der App zeigt der Tab Bar Controller kurz an, bevor das Segue ausgeführt wird. Ich habe versucht, den Code nach viewWillAppear zu verschieben, aber der Übergang funktioniert nicht so früh.
  • Wenn sie sich abmelden, befinden sich alle Daten noch in allen View Controllern. Wenn sie sich bei einem neuen Konto anmelden, werden die alten Kontodaten weiterhin angezeigt, bis sie aktualisiert werden. Ich brauche eine Möglichkeit, dies beim Abmelden einfach zu löschen.

Ich bin offen für eine Überarbeitung. Ich habe überlegt, den Anmeldebildschirm zum Root-Ansichts-Controller zu machen oder einen Navigations-Controller im AppDelegate zu erstellen, um alles zu handhaben. Ich bin mir nur nicht sicher, welche Methode derzeit die beste ist.

284
Trevor Gehman

Hier ist, was ich getan habe, um alles zu erreichen. Das einzige, was Sie zusätzlich berücksichtigen müssen, ist (a) der Anmeldevorgang und (b) der Ort, an dem Sie Ihre App-Daten speichern (in diesem Fall habe ich einen Singleton verwendet).

Storyboard showing login view controller and main tab controller

Wie Sie sehen können, ist der Controller für die Stammansicht mein Controller für die Hauptregisterkarte. Ich habe dies getan, weil ich möchte, dass die App nach dem Anmelden des Benutzers direkt auf der ersten Registerkarte gestartet wird. (Dadurch wird jegliches "Flimmern" vermieden, bei dem die Anmeldeansicht vorübergehend angezeigt wird.)

AppDelegate.m

In dieser Datei überprüfe ich, ob der Benutzer bereits angemeldet ist. Wenn nicht, drücke ich den Login-View-Controller. Ich übernehme auch den Abmeldevorgang, bei dem ich Daten lösche und die Anmeldeansicht zeige.

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

Wenn die Anmeldung erfolgreich ist, lasse ich die Ansicht einfach aus und sende eine Benachrichtigung.

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

In Ihrer appDelegate.m in Ihrem 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;
}

In der SignUpViewController.m-Datei

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

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

In der Datei 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 Version

didFinishLaunchingWithOptions im App-Delegaten unter der Annahme, dass Ihr anfänglicher Ansichtscontroller der in TabbarController signierte ist.

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

    return true

In Anmeldeansicht-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
308
bhavya kothari

EDIT: Abmeldeaktion hinzufügen.

enter image description here

1. Bereiten Sie zunächst die App-Delegat-Datei vor

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. Erstellen Sie eine Klasse mit dem Namen 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. Erstellen Sie einen neuen Controller RootViewController und verbinden Sie sich mit der ersten Ansicht, in der sich der Login-Button befindet. Fügen Sie auch eine Storyboard-ID hinzu: "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. Erstellen Sie einen neuen Controller LoginViewController und verbinden Sie sich mit der Login-Ansicht.

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. Fügen Sie am Ende einen neuen Controller ProfileViewController hinzu und verbinden Sie ihn mit der Profilansicht im 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 ist ein Beispielprojekt für zusätzliche Hilfe.

20

Die Antwort von bhavya gefiel mir nicht, da AppDelegate in View Controllers verwendet wurde und die Einstellung rootViewController keine Animation enthält. Und Trevors Antwort hat Probleme mit dem blinkenden View-Controller unter iOS8.

UPD 18.07.2015

AppDelegate in View Controllers:

Durch Ändern des AppDelegate-Status (der Eigenschaften) in View Controller wird die Kapselung unterbrochen.

Sehr einfache Hierarchie von Objekten in jedem iOS-Projekt:

AppDelegate (besitzt window und rootViewController)

ViewController (besitzt view)

Es ist in Ordnung, dass Objekte von oben Objekte von unten ändern, weil sie sie erstellen. Es ist jedoch nicht in Ordnung, wenn Objekte auf der Unterseite Objekte auf der Oberseite ändern (ich habe ein grundlegendes Programmier-/OOP-Prinzip beschrieben: DIP (Dependency Inversion Principle: High-Level-Modul darf nicht vom Low-Level-Modul abhängen, aber sie sollten von Abstraktionen abhängen). ).

Wenn ein Objekt ein Objekt in dieser Hierarchie ändert, kommt es früher oder später zu einem Durcheinander im Code. Bei den kleinen Projekten mag es in Ordnung sein, aber es macht keinen Spaß, sich durch dieses Durcheinander bei den Bit-Projekten zu wühlen =]

UPD 18.07.2015

Ich repliziere Modal Controller-Animationen mit UINavigationController (tl; dr: check the project ).

Ich verwende UINavigationController, um alle Controller in meiner App darzustellen. Anfangs habe ich den Login View Controller im Navigationsstapel mit einfacher Push/Pop-Animation angezeigt. Dann habe ich beschlossen, es mit minimalen Änderungen auf Modal umzustellen.

Wie es funktioniert:

  1. Initial View Controller (oder self.window.rootViewController) Ist UINavigationController mit ProgressViewController als rootViewController. Ich zeige ProgressViewController an, da die Initialisierung von DataModel einige Zeit in Anspruch nehmen kann, da es den Kerndatenstapel wie in diesem Beispiel enthält Artikel (Ich mag diesen Ansatz wirklich).

  2. AppDelegate ist für die Aktualisierung des Anmeldestatus verantwortlich.

  3. DataModel verwaltet die An- und Abmeldung von Benutzern und AppDelegate beobachtet die Eigenschaft userLoggedIn über KVO. Wohl nicht die beste Methode, um dies zu tun, aber es funktioniert für mich. (Warum KVO schlecht ist, können Sie this oder this article (Why Not Use Notifications? Part) einchecken.

  4. ModalDismissAnimator und ModalPresentAnimator werden zum Anpassen der Standard-Push-Animation verwendet.

So funktioniert die Logik des Animators:

  1. AppDelegate setzt sich als Delegat von self.window.rootViewController (Das ist UINavigationController).

  2. AppDelegate gibt bei Bedarf einen der Animatoren in -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:] Zurück.

  3. Animatoren implementieren die Methoden -transitionDuration: Und -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]];
         }];
    }
    

Testprojekt ist hier .

14
derpoliuk

Hier ist meine Swifty-Lösung für zukünftige Zuschauer.

1) Erstellen Sie ein Protokoll für die Anmelde- und Abmeldefunktionen:

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

2) Erweitern Sie das Protokoll und stellen Sie hier die Funktionalität zum Abmelden bereit:

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) Dann kann ich mein AppDelegate an das LoginFlowHandler-Protokoll anpassen und beim Start handleLogin aufrufen:

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
    }

}

Von hier aus übernimmt meine Protokollerweiterung die Logik oder ermittelt, ob der Benutzer angemeldet/abgemeldet ist, und ändert dann den Windows-RootViewController entsprechend!

10
Harry Bloom

Dies vom App-Delegaten aus zu tun, wird NICHT empfohlen. AppDelegate verwaltet den App-Lebenszyklus, der sich auf das Starten, Anhalten, Beenden usw. bezieht. Ich schlage vor, dies von Ihrem ursprünglichen Ansichtscontroller in viewDidAppear aus zu tun. Du kannst self.presentViewController und self.dismissViewController von der Anmeldeansichtsteuerung. Speichern Sie einen bool -Schlüssel in NSUserDefaults, um festzustellen, ob er zum ersten Mal gestartet wird.

8
Mihado

In Xcode 7 können Sie mehrere Storyboards haben. Es ist besser, wenn Sie den Anmeldefluss in einem separaten Storyboard speichern können.

Dies kann mit erfolgen SELECT VIEWCONTROLLER> Editor> Refactor zum Storyboard

Und hier ist die Swift Version zum Einstellen einer Ansicht als 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**.

Nachdem Sie den LoginViewController und TabBarController erstellt haben, müssen Sie eine StoryboardID als hinzufügen " loginViewController" bzw. " tabBarController".

Dann erstelle ich lieber die Konstante Struktur:

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

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

In LoginViewController hinzufügen IBAction:

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

In ProfileViewController add IBAction:

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

In AppDelegate Codezeile hinzufügen in didFinishLaunchingWithOptions:

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

    Switcher.updateRootViewController()

    return true
}

Zum Schluss erstellen Sie eine Switcher-Klasse :

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

    }

}

Das ist alles!

4
iAleksandr

Ich benutze dies, um nach dem ersten Start zu suchen:

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

(Wenn der Benutzer die App löscht und erneut installiert, wird sie wie ein erster Start gezählt.)

In der AppDelegate überprüfe ich den ersten Start und erstelle einen Navigations-Controller mit den Anmeldebildschirmen (Anmelden und Registrieren), die ich über das aktuelle Hauptfenster lege:

[self.window makeKeyAndVisible];

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

Da dies über dem regulären Ansichtscontroller liegt, ist es unabhängig vom Rest Ihrer App und Sie können den Ansichtscontroller einfach schließen, wenn Sie ihn nicht mehr benötigen. Und Sie können die Ansicht auch auf diese Weise darstellen, wenn der Benutzer manuell eine Taste drückt.

Übrigens: Ich speichere die Login-Daten meiner Benutzer wie folgt:

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

Zum Abmelden: Ich habe mich von CoreData abgewendet (zu langsam) und verwalte meine Daten jetzt mit NSArrays und NSDictionaries. Abmelden bedeutet nur, diese Arrays und Wörterbücher zu leeren. Außerdem stelle ich sicher, dass meine Daten in viewWillAppear festgelegt sind.

Das ist es.

3
Thorsten

Ich hatte ein ähnliches Problem in einer App zu lösen und ich habe die folgende Methode verwendet. Ich habe keine Benachrichtigungen für die Navigation verwendet.

Ich habe drei Storyboards in der App.

  1. Startbildschirm-Storyboard - zur App-Initialisierung und Überprüfung, ob der Benutzer bereits angemeldet ist
  2. Login-Storyboard - zur Verwaltung des Benutzeranmeldeflusses
  3. Registerkarten-Storyboard - zum Anzeigen des App-Inhalts

Mein erstes Storyboard in der App ist das Splash Screen Storyboard. Ich verwende den Navigations-Controller als Stammverzeichnis für die Anmeldung und das Storyboard für die Registerkartenleiste, um die Navigation durch den Ansichts-Controller zu verwalten.

Ich habe eine Navigator-Klasse erstellt, um die App-Navigation zu verwalten. Sie sieht folgendermaßen aus:

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)

           }

       }

   }

}

Schauen wir uns die möglichen Szenarien an:

  • Erster App-Start; Der Begrüßungsbildschirm wird geladen, in dem ich überprüfe, ob der Benutzer bereits angemeldet ist. Anschließend wird der Anmeldebildschirm mithilfe der Navigator-Klasse wie folgt geladen.

Da ich den Navigationscontroller als Root habe, instanziiere ich den Navigationscontroller als Initial View Controller.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Dadurch wird das Slpash-Storyboard aus dem Stammverzeichnis des App-Fensters entfernt und durch das Login-Storyboard ersetzt.

Wenn sich der Benutzer im Anmeldestoryboard erfolgreich angemeldet hat, speichere ich die Benutzerdaten in den Benutzervorgaben und initialisiere ein UserData-Singleton, um auf die Benutzerdetails zuzugreifen. Anschließend wird das Storyboard der Tab-Leiste mit der Navigator-Methode geladen.

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

Navigator.moveTo(tabBarNav, from: self)

Der Benutzer meldet sich jetzt vom Einstellungsbildschirm in der Registerkartenleiste ab. Ich lösche alle gespeicherten Benutzerdaten und navigiere zum Anmeldebildschirm.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • Der Benutzer ist angemeldet und erzwingt das Beenden der App

Wenn der Benutzer die App startet, wird der Begrüßungsbildschirm geladen. Ich überprüfe, ob der Benutzer angemeldet ist und greife über die Benutzervorgaben auf die Benutzerdaten zu. Initialisieren Sie dann das UserData-Singleton und zeigen Sie die Registerkartenleiste anstelle des Anmeldebildschirms an.

0
Jithin

Danke, Bhavya. Es gab zwei Antworten über Swift, aber diese sind nicht sehr intakt. Ich habe das im Swift3 gemacht. Unten ist der Hauptcode.

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

In SignUpViewController.Swift

@IBAction func userLogin(_ sender: UIButton) {
    //handle your login work
    UserDefaults.standard.setValue(true, forKey: Common.isLoginKey)
    let delegateTemp = UIApplication.shared.delegate
    delegateTemp?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Main")
}

In logOutAction Funktion

@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

Ich bin in der gleichen Situation wie Sie und die Lösung, die ich gefunden habe, um die Daten zu bereinigen, besteht darin, alle CoreData-Daten zu löschen, auf die sich meine View-Controller verlassen, um ihre Informationen zu zeichnen. Ich fand diesen Ansatz jedoch immer noch sehr schlecht. Ich denke, dass eine elegantere Methode dafür ohne Storyboards und mit nur Code zum Verwalten der Übergänge zwischen Ansichtscontrollern möglich ist.

Ich habe dieses Projekt bei Github gefunden, das all diese Dinge nur per Code macht und es ist ziemlich einfach zu verstehen. Sie verwenden ein Facebook-ähnliches Seitenmenü und ändern den Center-View-Controller, je nachdem, ob der Benutzer angemeldet ist oder nicht. Wenn der Benutzer sich abmeldet, entfernt appDelegate die Daten aus CoreData und setzt den Hauptansichts-Controller wieder auf den Anmeldebildschirm.

0
amb