Mejores prácticas para la pantalla de inicio de sesión de Storyboard, manejo de borrado de datos al finalizar la sesión

Estoy construyendo una aplicación para iOS usando Storyboard. El controlador de vista raíz es un controlador de barra de tabs. Estoy creando el proceso de inicio de sesión / cierre de sesión, y en general funciona bien, pero tengo algunos problemas. Necesito saber la MEJOR manera de configurar todo esto.

Quiero lograr lo siguiente:

  1. Muestre una pantalla de inicio de sesión la primera vez que se inicia la aplicación. Cuando inician sesión, vaya a la primera pestaña del Controlador de la barra de tabs.
  2. Cada vez que inicien la aplicación después de eso, verifique si han iniciado sesión y salte directamente a la primera pestaña del controlador de la barra de tabs.
  3. Cuando hacen clic manualmente en un botón de cerrar sesión, muestran la pantalla de inicio de sesión y borran todos los datos de los controladores de vista.

Lo que he hecho hasta ahora es configurar el controlador de la vista raíz en el controlador de la barra de tabs, y crear una transición personalizada a mi controlador de vista de inicio de sesión. Dentro de mi clase de Controlador de barra de tabs, compruebo si están conectados dentro del método viewDidAppear y realizo el segue: [self performSegueWithIdentifier:@"pushLogin" sender:self];

También configuro una notificación para cuando se debe realizar la acción de [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil]; : [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

Al cerrar la sesión, [self setSelectedIndex:0] las credenciales de Keychain, ejecuto [self setSelectedIndex:0] y realizo el cambio para mostrar nuevamente el controlador de vista de inicio de sesión.

Todo esto funciona bien, pero me pregunto: ¿ debería esta lógica estar en AppDelegate? También tengo dos problemas:

  • La primera vez que inician la aplicación , el controlador de la barra de tabs se muestra brevemente antes de realizar la transición. Intenté mover el código para ver viewWillAppear pero el cambio no funcionará tan temprano.
  • Cuando finalizan la sesión, todos los datos todavía están dentro de todos los controladores de vista. Si inician sesión en una cuenta nueva, los datos de la cuenta anterior se mostrarán hasta que se actualicen. Necesito una forma de borrar esto fácilmente al cerrar la sesión.

Estoy abierto a volver a trabajar esto. Consideré convertir la pantalla de inicio de sesión en el controlador de la vista raíz o crear un controlador de navegación en AppDelegate para manejar todo … No estoy seguro de cuál es el mejor método en este momento.

Tu guión gráfico debería verse así

En su appDelegate.m dentro de su 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; } 

En el archivo SignUpViewController.m

 - (IBAction)actionSignup:(id)sender { AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate]; appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController]; } 

En el archivo 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; } 

Versión Swift 4

didFinishLaunchingWithOptions en el delegado de la aplicación suponiendo que su controlador de vista inicial es el firmado en TabbarController.

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

En el controlador de vista de registro:

 @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 

Esto es lo que terminé haciendo para lograr todo. Lo único que debe considerar además de esto es (a) el proceso de inicio de sesión y (b) dónde está almacenando los datos de su aplicación (en este caso, utilicé un singleton).

Storyboard que muestra el controlador de vista de inicio de sesión y el controlador de pestañas principal

Como puede ver, el controlador de vista raíz es mi Controlador de pestaña principal . Hice esto porque después de que el usuario haya iniciado sesión, quiero que la aplicación se inicie directamente en la primera pestaña. (Esto evita cualquier “parpadeo” cuando la vista de inicio de sesión se muestra temporalmente).

AppDelegate.m

En este archivo, verifico si el usuario ya está conectado. De lo contrario, presiono el controlador de vista de inicio de sesión. También manejo el proceso de cierre de sesión, donde borro los datos y muestro la vista de inicio de sesión.

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

Aquí, si el inicio de sesión es exitoso, simplemente descarto la vista y envío una notificación.

 -(void) loginWasSuccessful { // Send notification [[NSNotificationCenter defaultCenter] postNotificationName:@"loginSuccessful" object:self]; // Dismiss login screen [self dismissViewControllerAnimated:YES completion:nil]; } 

EDITAR: Agregar acción de cierre de sesión.

enter image description here

1. Antes que nada prepare el archivo delegado de la aplicación

AppDelegate.h

 #import  @interface AppDelegate : UIResponder  @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. Crea una clase llamada Usuario.

Usuario.h

 #import  @interface User : NSObject - (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password; - (void)logout; - (BOOL)userAuthenticated; @end 

Usuario.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. Cree un nuevo controlador RootViewController y conectado con la primera vista, donde el botón de inicio de sesión en vivo. Añada también una ID del guión gráfico: “vista inicial”.

RootViewController.h

 #import  #import "LoginViewController.h" @protocol LoginViewProtocol  - (void)dismissAndLoginView; @end @interface RootViewController : UIViewController @property (nonatomic, weak) id  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. Cree un nuevo controlador LoginViewController y conectado con la vista de inicio de sesión.

LoginViewController.h

 #import  #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. Al final, agregue un nuevo controlador ProfileViewController y conéctese con la vista de perfil en tabController.

ProfileViewController.h

 #import  @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 es un proyecto de muestra para obtener ayuda adicional.

No me gustó la respuesta de bhavya porque usé AppDelegate dentro de View Controllers y la configuración de rootViewController no tiene animación. Y la respuesta de Trevor tiene problemas con el controlador de visualización parpadeante en iOS8.

UPD 18/07/2015

AppDelegate dentro de View Controllers:

Al cambiar el estado (propiedades) de AppDelegate dentro del controlador de vista, se rompe la encapsulación.

Jerarquía de objetos muy simple en cada proyecto de iOS:

AppDelegate (posee window y rootViewController )

ViewController (posee view )

Está bien que los objetos de la parte superior cambien los objetos en la parte inferior, porque los están creando. Pero no está bien si los objetos en la parte inferior cambian objetos encima de ellos (describí algunos principios básicos de progtwigción / OOP: DIP (Principio de Inversión de Dependencia: el módulo de alto nivel no debe depender del módulo de bajo nivel, sino que deben depender de abstracciones) )

Si algún objeto cambiará algún objeto en esta jerarquía, tarde o temprano habrá un desorden en el código. Puede estar bien en los proyectos pequeños, pero no es divertido excavar este desastre en los proyectos de bits =]

UPD 18/07/2015

UINavigationController animaciones de controlador modal usando UINavigationController (tl; dr: verificar el proyecto ).

Estoy usando UINavigationController para presentar todos los controladores en mi aplicación. Inicialmente, mostré el controlador de vista de inicio de sesión en la stack de navegación con una animación simple push / pop. Entonces decidí cambiarlo a modal con cambios mínimos.

Cómo funciona:

  1. Controlador de vista inicial (o self.window.rootViewController ) es UINavigationController con ProgressViewController como un rootViewController . Estoy mostrando ProgressViewController porque DataModel puede tardar un tiempo en inicializarse porque se encuentra en su stack de datos centrales como en este artículo (realmente me gusta este enfoque).

  2. AppDelegate es responsable de obtener actualizaciones de estado de inicio de sesión.

  3. DataModel maneja el inicio / cierre de sesión del usuario y AppDelegate está observando su propiedad userLoggedIn a través de KVO. Podría decirse que no es el mejor método para hacerlo, pero funciona para mí. (Por qué KVO es malo, puede consultar este o este artículo (¿Por qué no utilizar las notificaciones?).

  4. ModalDismissAnimator y ModalPresentAnimator se utilizan para personalizar la animación push predeterminada.

Cómo funciona la lógica de animadores:

  1. AppDelegate se establece como un delegado de self.window.rootViewController (que es UINavigationController).

  2. AppDelegate devuelve uno de los animadores en -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:] si es necesario.

  3. Los animadores implementan -transitionDuration: y -animateTransition: methods. -[ModalPresentAnimator animateTransition:] :

     - (void)animateTransition:(id)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]]; }]; } 

El proyecto de prueba está aquí .

No se recomienda hacer esto desde el delegado de la aplicación. AppDelegate administra el ciclo de vida de la aplicación relacionado con el inicio, la suspensión, la terminación, etc. Sugiero hacer esto desde su controlador de vista inicial en viewDidAppear . Puede self.presentViewController y self.dismissViewController desde el controlador de vista de inicio de sesión. Almacene una clave bool en NSUserDefaults para ver si se está iniciando por primera vez.

Aquí está mi solución Swifty para cualquier espectador futuro.

1) Cree un protocolo para manejar las funciones de inicio de sesión y cierre de sesión:

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

2) Extienda dicho protocolo y proporcione la funcionalidad aquí para cerrar la sesión:

 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) Luego puedo adaptar mi AppDelegate al protocolo LoginFlowHandler y llamar a handleLogin al inicio:

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

Desde aquí, mi extensión de protocolo manejará la lógica o determinará si el usuario está conectado / desconectado, y luego cambiará el controlador raíz de Windows según corresponda.

En Xcode 7 puedes tener múltiples storyboys. Será mejor si puede mantener el flujo de inicio de sesión en un guión gráfico independiente.

Esto puede hacerse utilizando SELECT VIEWCONTROLLER> Editor> Refactor a Storyboard

Y aquí está la versión Swift para establecer una vista como RootViewContoller-

  let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate appDelegate.window!.rootViewController = newRootViewController let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController") 

Lo uso para verificar el primer lanzamiento:

 - (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 (eg, 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 el usuario elimina la aplicación y la vuelve a instalar, se considera como un primer lanzamiento)

En AppDelegate compruebo el primer lanzamiento y creo un controlador de navegación con las pantallas de inicio de sesión (inicio de sesión y registro), que coloco en la parte superior de la ventana principal actual:

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

Como esto está en la parte superior del controlador de vista regular, es independiente del rest de su aplicación y puede descartar el controlador de vista, si ya no lo necesita. Y también puede presentar la vista de esta manera, si el usuario presiona un botón manualmente.

Por cierto: guardo los datos de inicio de sesión de mis usuarios de esta manera:

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

Para el cierre de sesión: me alejé de CoreData (demasiado lento) y uso NSArrays y NSDictionaries para administrar mis datos ahora. Salir solo significa vaciar esas matrices y diccionarios. Además, me aseguro de configurar mis datos en viewWillAppear.

Eso es.

Estoy en la misma situación que tú y la solución que encontré para limpiar los datos es eliminar todos los elementos de CoreData en los que confían mis controladores de vista para extraer su información. Pero todavía encuentro que este enfoque es muy malo, creo que se puede lograr una forma más elegante de hacerlo sin guiones gráficos y usando solo código para administrar las transiciones entre los controladores de vista.

Encontré este proyecto en Github que hace todo esto solo por código y es bastante fácil de entender. Utilizan un menú lateral similar a Facebook y lo que hacen es cambiar el controlador de vista central dependiendo de si el usuario está conectado o no. Cuando el usuario cierra la sesión, appDelegate elimina los datos de CoreData y vuelve a configurar el controlador de vista principal en la pantalla de inicio de sesión.

Gracias a la solución de bhavya. Hubo dos respuestas rápidas, pero esas no están muy intactas. Tengo que hacer eso en el swift3. A continuación está el código principal.

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

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

En la función logOutAction

 @IBAction func logOutAction(_ sender: UIButton) { UserDefaults.standard.setValue(false, forKey: Common.isLoginKey) UIApplication.shared.delegate?.window!?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() } 

Tuve un problema similar para resolver en una aplicación y utilicé el siguiente método. No usé notificaciones para manejar la navegación.

Tengo tres guiones gráficos en la aplicación.

  1. Guión gráfico de la pantalla de bienvenida: para la inicialización de la aplicación y comprobar si el usuario ya ha iniciado sesión
  2. Guión gráfico de inicio de sesión: para controlar el flujo de inicio de sesión de usuario
  3. Guión gráfico de barra de tabulación: para mostrar el contenido de la aplicación

Mi guión gráfico inicial en la aplicación es el guión gráfico de la pantalla Splash. Tengo el controlador de navegación como la raíz del guión gráfico de inicio de sesión y barra de tabs para manejar las navegaciones del controlador de vista.

Creé una clase Navigator para manejar la navegación de la aplicación y se ve así:

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

Veamos los posibles escenarios:

  • Primer lanzamiento de la aplicación; La pantalla de bienvenida se cargará cuando compruebe si el usuario ya inició sesión. Luego, la pantalla de inicio de sesión se cargará utilizando la clase Navigator de la siguiente manera;

Como tengo el controlador de navegación como raíz, instanciamos el controlador de navegación como controlador de vista inicial.

 let loginSB = UIStoryboard(name: "splash", bundle: nil) let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController Navigator.moveTo(loginNav, from: self) 

Esto elimina el guión gráfico slpash de la raíz de la aplicación y lo reemplaza con el guión gráfico de inicio de sesión.

Desde el guión gráfico de inicio de sesión, cuando el usuario inició sesión correctamente, guardo los datos del usuario en Predeterminados del usuario e inicializo un singleton UserData para acceder a los detalles del usuario. A continuación, el guión gráfico de la barra de tabs se carga utilizando el método de navegación.

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

Ahora el usuario cierra la sesión desde la pantalla de configuración en la barra de tabs. Borro todos los datos guardados del usuario y navego a la pantalla de inicio de sesión.

 let loginSB = UIStoryboard(name: "splash", bundle: nil) let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController Navigator.moveTo(loginNav, from: self) 
  • El usuario ha iniciado sesión y la fuerza mata la aplicación

Cuando el usuario inicie la aplicación, se cargará la pantalla de bienvenida. Verifico si el usuario está conectado y accedo a los datos del usuario desde Valores Predeterminados del Usuario. A continuación, inicialice el singleton UserData y muestre la barra de tabs en lugar de la pantalla de inicio de sesión.

Crea ** LoginViewController ** y ** TabBarController **.

Después de crear el LoginViewController y el TabBarController , necesitamos agregar un StoryboardID como ” loginViewController ” y ” tabBarController ” respectivamente.

Entonces prefiero crear la estructura constante :

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

En LoginViewController, agregue IBAction :

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

En ProfileViewController, agregue IBAction :

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

En AppDelegate, agregue una línea de código en didFinishLaunchingWithOptions :

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { Switcher.updateRootViewController() return true } 

Finalmente crea la clase 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 } } 

¡Eso es todo!

enter image description here

En App Delegate.m

  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault]; NSString *identifier; BOOL isSaved = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginSaved"]; if (isSaved) { //identifier=@"homeViewControllerId"; UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window]; UITabBarController *tabBarVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"]; mainWindow.rootViewController=tabBarVC; } else { identifier=@"loginViewControllerId"; UIStoryboard * storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil]; UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:identifier]; UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:screen]; self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; } return YES; 

}

view controller.m In view did load

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:@selector(logoutButtonClicked:)]; [self.navigationItem setLeftBarButtonItem:barButton]; 

}

In logout button action

 -(void)logoutButtonClicked:(id)sender{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Do you want to logout?" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setBool:NO forKey:@"loginSaved"]; [[NSUserDefaults standardUserDefaults] synchronize]; AppDelegate *appDelegate = [UIApplication sharedApplication].delegate; UIStoryboard * storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil]; UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:@"loginViewControllerId"]; [appDelegate.window setRootViewController:screen]; }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [self dismissViewControllerAnimated:YES completion:nil]; }]]; dispatch_async(dispatch_get_main_queue(), ^ { [self presentViewController:alertController animated:YES completion:nil]; });} 
    Intereting Posts