Лучшие практики для экрана регистрации раскадровки, обработка очистки данных при выходе из системы

Я создаю приложение iOS с помощью раскадровки. Контроллер корневого представления представляет собой контроллер панели вкладок. Я создаю процесс входа/выхода из системы, и он в основном работает нормально, но у меня есть несколько проблем. Мне нужно знать ЛУЧШИЙ способ установить все это.

Я хочу выполнить следующее:

  • Показать экран входа в систему при первом запуске приложения. Когда они войдут в систему, перейдите на первую вкладку контроллера панели вкладок.
  • При каждом запуске приложения после этого проверьте, вошли ли они в систему, и пропустите прямо к первой вкладке корневого контроллера панели.
  • Когда они вручную нажимают кнопку выхода из системы, показывают экран входа в систему и очищают все данные от контроллеров представления.

То, что я сделал до сих пор, - установить контроллер корневого представления в контроллер панели вкладок и создать пользовательский сеанс для моего контроллера входа в систему. Внутри моего класса "Контроллер панели вкладок" я проверяю, вошли ли они внутри метода viewDidAppear, и выполните сеанс: [self performSegueWithIdentifier:@"pushLogin" sender:self];

Я также настраиваю уведомление о том, когда необходимо выполнить действие выхода из системы: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

При выходе из системы я удаляю учетные данные из Keychain, запускаю [self setSelectedIndex:0] и выполняю segue, чтобы снова отобразить контроллер входа в систему.

Все работает отлично, но мне интересно: должна ли эта логика быть в AppDelegate? У меня также есть две проблемы:

  • При первом запуске приложения контроллер панели вкладок показывается ненадолго перед выполнением segue. Я попытался перевести код на viewWillAppear, но segue не будет работать так рано.
  • Когда они выходят из системы, все данные все еще находятся внутри всех контроллеров представления. Если они войдут в новую учетную запись, старые данные учетной записи будут отображаться до тех пор, пока они не будут обновлены. Мне нужно, чтобы это было легко устранить при выходе из системы.

Я готов переработать это. Я рассмотрел возможность сделать экран входа в систему контроллером корневого представления или создать контроллер навигации в AppDelegate для обработки всего... Я просто не уверен, какой лучший метод на данный момент.

Ответ 1

Вот что я в итоге сделал, чтобы выполнить все. Единственное, что вам нужно учитывать в дополнение к этому: (a) процесс входа в систему и (b) где вы храните свои данные приложения (в этом случае я использовал одноэлементный).

Storyboard showing login view controller and main tab controller

Как вы можете видеть, контроллер корневого представления - это мой главный табулятор. Я сделал это, потому что после того, как пользователь вошел в систему, я хочу, чтобы приложение запускалось непосредственно на первую вкладку. (Это позволяет избежать любого "мерцания", когда временно открывается окно входа в систему.)

AppDelegate.m

В этом файле я проверяю, был ли пользователь уже зарегистрирован. Если нет, я нажимаю контроллер входа в систему. Я также обрабатываю процесс выхода из системы, где я очищаю данные и показываю вид входа.

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

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

    return YES;
}

-(void) showLoginScreen:(BOOL)animated
{

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

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

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

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

}

LoginViewController.m

Здесь, если вход успешно завершен, я просто отклоняю представление и отправляю уведомление.

-(void) loginWasSuccessful
{

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

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

}

Ответ 2

Your storyboard should look like this

В вашем приложении appDelegate.m внутри вашего файла 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;
}

В файле SignUpViewController.m

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

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

В файле MyTabThreeViewController.m

- (IBAction)actionLogout:(id)sender {

    // Delete User credential from NSUserDefaults and other data related to user

    AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];

    UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"];

    UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController];
    appDelegateTemp.window.rootViewController = navigation;

}

Версия Swift 4

didFinishLaunchingWithOptions в делетете приложения, предполагая, что ваш контроллер начального представления подписан в TabbarController.

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

    return true

В контроллере просмотра:

@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}

MyTabThreeViewController

 //Remove user credentials
guard let appDel = UIApplication.shared.delegate as? AppDelegate else { return }
        let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "WelcomeNavigation")
        appDel.window?.rootViewController = rootController

Ответ 3

EDIT: Добавить действие выхода из системы.

enter image description here

1. Прежде всего подготовьте файл делегата приложения

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic) BOOL authenticated;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "User.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    User *userObj = [[User alloc] init];
    self.authenticated = [userObj userAuthenticated];

    return YES;
}

2. Создайте класс с именем User.

user.h

#import <Foundation/Foundation.h>

@interface User : NSObject

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password;
- (void)logout;
- (BOOL)userAuthenticated;

@end

User.m

#import "User.h"

@implementation User

- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password{

    // Validate user here with your implementation
    // and notify the root controller
    [[NSNotificationCenter defaultCenter] postNotificationName:@"loginActionFinished" object:self userInfo:nil];
}

- (void)logout{
    // Here you can delete the account
}

- (BOOL)userAuthenticated {

    // This variable is only for testing
    // Here you have to implement a mechanism to manipulate this
    BOOL auth = NO;

    if (auth) {
        return YES;
    }

    return NO;
}

3. Создайте новый контроллер RootViewController и подключитесь к первому представлению, в котором активна кнопка входа. Добавьте также идентификатор раскадровки: "initialView".

RootViewController.h

#import <UIKit/UIKit.h>
#import "LoginViewController.h"

@protocol LoginViewProtocol <NSObject>

- (void)dismissAndLoginView;

@end

@interface RootViewController : UIViewController

@property (nonatomic, weak) id <LoginViewProtocol> delegate;
@property (nonatomic, retain) LoginViewController *loginView;


@end

RootViewController.m

#import "RootViewController.h"

@interface RootViewController ()

@end

@implementation RootViewController

@synthesize loginView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)loginBtnPressed:(id)sender {

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loginActionFinished:)
                                                 name:@"loginActionFinished"
                                               object:loginView];

}

#pragma mark - Dismissing Delegate Methods

-(void) loginActionFinished:(NSNotification*)notification {

    AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    authObj.authenticated = YES;

    [self dismissLoginAndShowProfile];
}

- (void)dismissLoginAndShowProfile {
    [self dismissViewControllerAnimated:NO completion:^{
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UITabBarController *tabView = [storyboard instantiateViewControllerWithIdentifier:@"profileView"];
        [self presentViewController:tabView animated:YES completion:nil];
    }];


}

@end

4. Создайте новый контроллер LoginViewController и подключитесь к нему.

LoginViewController.h

#import <UIKit/UIKit.h>
#import "User.h"

@interface LoginViewController : UIViewController

LoginViewController.m

#import "LoginViewController.h"
#import "AppDelegate.h"

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)submitBtnPressed:(id)sender {
    User *userObj = [[User alloc] init];

    // Here you can get the data from login form
    // and proceed to authenticate process
    NSString *username = @"username retrieved through login form";
    NSString *password = @"password retrieved through login form";
    [userObj loginWithUsername:username andPassword:password];
}

@end

5. В конце добавьте новый контроллер ProfileViewController и подключитесь к представлению профиля в tabViewController.

ProfileViewController.h

#import <UIKit/UIKit.h>

@interface ProfileViewController : UIViewController

@end

ProfileViewController.m

#import "ProfileViewController.h"
#import "RootViewController.h"
#import "AppDelegate.h"
#import "User.h"

@interface ProfileViewController ()

@end

@implementation ProfileViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

}

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if(![(AppDelegate*)[[UIApplication sharedApplication] delegate] authenticated]) {

        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

        RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
        [initView setModalPresentationStyle:UIModalPresentationFullScreen];
        [self presentViewController:initView animated:NO completion:nil];
    } else{
        // proceed with the profile view
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)logoutAction:(id)sender {

   User *userObj = [[User alloc] init];
   [userObj logout];

   AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate];
   authObj.authenticated = NO;

   UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

   RootViewController *initView =  (RootViewController*)[storyboard instantiateViewControllerWithIdentifier:@"initialView"];
   [initView setModalPresentationStyle:UIModalPresentationFullScreen];
   [self presentViewController:initView animated:NO completion:nil];

}

@end

LoginExample - пример проекта для дополнительной помощи.

Ответ 4

Мне не понравился ответ bhavya из-за использования AppDelegate внутри View Controllers и установка rootViewController не имеет анимации. И ответ Тревора имеет проблему с мигающим контроллером просмотров на iOS8.

UPD 07/18/2015

AppDelegate внутри контроллеров просмотра:

Изменение состояния (свойств) AppDelegate внутри контроллера просмотра прерывает инкапсуляцию.

Очень простая иерархия объектов в каждом проекте iOS:

AppDelegate (владеет window и rootViewController)

ViewController (владеет view)

Это нормально, что объекты из верхних объектов изменения внизу, потому что они их создают. Но это не нормально, если объекты на дне меняют объекты поверх них (я описал некоторые основные принципы программирования/ООП: DIP (принцип инверсии зависимостей: модуль высокого уровня не должен зависеть от модуля низкого уровня, но они должны зависеть от абстракций)).

Если какой-либо объект изменит любой объект в этой иерархии, рано или поздно в коде будет беспорядок. Это может быть хорошо на небольших проектах, но это не забавно прорывать этот беспорядок на бит-проектах =]

UPD 07/18/2015

Я реплицирую анимацию модального контроллера с помощью UINavigationController (tl; dr: проверьте проект).

Я использую UINavigationController для представления всех контроллеров в своем приложении. Первоначально я отображал контроллер входа в навигационный стек с простой анимацией push/pop. Затем я решил изменить его на модальный с минимальными изменениями.

Как это работает:

  • Контроллер начального представления (или self.window.rootViewController) является UINavigationController с ProgressViewController как rootViewController. Я показываю ProgressViewController, потому что DataModel может занять некоторое время, чтобы инициализировать, потому что он входит в основной стек данных, как в этом article (Мне очень нравится этот подход).

  • AppDelegate несет ответственность за получение обновлений статуса входа.

  • DataModel обрабатывает вход пользователя/выхода из системы, и AppDelegate наблюдает его свойство userLoggedIn через KVO. Вероятно, это не лучший способ сделать это, но он работает для меня. (Почему KVO плохой, вы можете проверить this или эта статья (почему не использовать оповещения? часть).

  • ModalDismissAnimator и ModalPresentAnimator используются для настройки анимации по умолчанию по умолчанию.

Как работает логика аниматоров:

  • AppDelegate устанавливает себя как делегат self.window.rootViewController (который является UINavigationController).

  • AppDelegate при необходимости возвращает один из аниматоров в -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:].

  • Аниматоры реализуют методы -transitionDuration: и -animateTransition:. -[ModalPresentAnimator animateTransition:]:

    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        [[transitionContext containerView] addSubview:toViewController.view];
        CGRect frame = toViewController.view.frame;
        CGRect toFrame = frame;
        frame.origin.y = CGRectGetHeight(frame);
        toViewController.view.frame = frame;
        [UIView animateWithDuration:[self transitionDuration:transitionContext]
                         animations:^
         {
             toViewController.view.frame = toFrame;
         } completion:^(BOOL finished)
         {
             [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
         }];
    }
    

Проект тестирования здесь.

Ответ 5

Здесь мое Swifty решение для любых будущих зрителей.

1) Создайте протокол для обработки функций входа и выхода:

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

2) Расширить указанный протокол и предоставить здесь функциональность для выхода из системы:

extension LoginFlowHandler {

    func handleLogin(withWindow window: UIWindow?) {

        if let _ = AppState.shared.currentUserId {
            //User has logged in before, cache and continue
            self.showMainApp(withWindow: window)
        } else {
            //No user information, show login flow
            self.showLogin(withWindow: window)
        }
    }

    func handleLogout(withWindow window: UIWindow?) {

        AppState.shared.signOut()

        showLogin(withWindow: window)
    }

    func showLogin(withWindow window: UIWindow?) {
        window?.subviews.forEach { $0.removeFromSuperview() }
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.login.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

    func showMainApp(withWindow window: UIWindow?) {
        window?.rootViewController = nil
        window?.rootViewController = R.storyboard.mainTabBar.instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }

}

3) Затем я могу согласовать свой AppDelegate с протоколом LoginFlowHandler и вызвать handleLogin при запуске:

class AppDelegate: UIResponder, UIApplicationDelegate, LoginFlowHandler {

    var window: UIWindow?

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

        window = UIWindow.init(frame: UIScreen.main.bounds)

        initialiseServices()

        handleLogin(withWindow: window)

        return true
    }

}

Отсюда мое расширение протокола будет обрабатывать логику или определять, будет ли пользователь входить/выходить, а затем соответствующим образом изменять окна rootViewController!

Ответ 6

Выполнение этого из делегата приложения НЕ рекомендуется. AppDelegate управляет жизненным циклом приложения, который связан с запуском, приостановкой, завершением и т.д. Я предлагаю сделать это с вашего начального контроллера представления в viewDidAppear. Вы можете self.presentViewController и self.dismissViewController от контроллера входа в систему. Сохраните ключ bool в NSUserDefaults, чтобы увидеть, запускается ли он впервые.

Ответ 7

В Xcode 7 вы можете иметь несколько storyBoards. Будет лучше, если вы сможете оставить поток входа в отдельную раскадровку.

Это можно сделать, используя SELECT VIEWCONTROLLER > Редактоp > Рефактор для раскадровки

И вот версия Swift для установки представления как RootViewContoller-

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

    let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController")

Ответ 8

Я использую это для проверки первого запуска:

- (NSInteger) checkForFirstLaunch
{
    NSInteger result = 0; //no first launch

    // Get current version ("Bundle Version") from the default Info.plist file
    NSString *currentVersion = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
    NSArray *prevStartupVersions = [[NSUserDefaults standardUserDefaults] arrayForKey:@"prevStartupVersions"];
    if (prevStartupVersions == nil)
    {
        // Starting up for first time with NO pre-existing installs (e.g., fresh
        // install of some version)
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:currentVersion] forKey:@"prevStartupVersions"];
        result = 1; //first launch of the app
    } else {
        if (![prevStartupVersions containsObject:currentVersion])
        {
            // Starting up for first time with this version of the app. This
            // means a different version of the app was alread installed once
            // and started.
            NSMutableArray *updatedPrevStartVersions = [NSMutableArray arrayWithArray:prevStartupVersions];
            [updatedPrevStartVersions addObject:currentVersion];
            [[NSUserDefaults standardUserDefaults] setObject:updatedPrevStartVersions forKey:@"prevStartupVersions"];
            result = 2; //first launch of this version of the app
        }
    }

    // Save changes to disk
    [[NSUserDefaults standardUserDefaults] synchronize];

    return result;
}

(если пользователь удаляет приложение и переустанавливает его, он считается первым запуском)

В AppDelegate я проверяю первый запуск и создаю навигационный контроллер с экранами входа (вход и регистрация), которые я накладываю поверх текущего главного окна:

[self.window makeKeyAndVisible];

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

Поскольку это находится поверх обычного контроллера просмотра, он не зависит от остальной части вашего приложения, и вы можете просто закрыть диспетчер представлений, если он вам больше не нужен. И вы также можете представить представление таким образом, если пользователь нажимает кнопку вручную.

BTW: Я сохраняю данные для входа от своих пользователей следующим образом:

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

Для выхода: я переключился с CoreData (слишком медленно) и теперь использую NSArrays и NSdictionaries для управления моими данными. Выход из системы - это просто, чтобы опорожнить эти массивы и словари. Кроме того, я уверен, чтобы установить мои данные в viewWillAppear.

Что это.

Ответ 9

Я в той же ситуации, что и вы, и решение, которое я нашел для очистки данных, - это удаление всего материала CoreData, на который полагаются мои контроллеры представлений, чтобы привлечь его информацию. Но я все же считаю, что этот подход очень плох, я думаю, что более элегантный способ сделать это можно выполнить без раскадровки и использовать только код для управления переходами между контроллерами представлений.

Я нашел этот проект в Github, который делает все это только по коду, и это довольно легко понять. Они используют Facebook-подобное боковое меню, и то, что они делают, это изменение контроллера центра в зависимости от того, вошел ли пользователь в систему или нет. Когда пользователь выходит из системы, appDelegate удаляет данные из CoreData и снова переключает главный контроллер представления на экран входа.

Ответ 10

Спасибо bhavya solution.There были два ответа о swift, но они не очень неповрежденные. Я делаю это в swift3.Below - основной код.

В AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    // seclect the mainStoryBoard entry by whthere user is login.
    let userDefaults = UserDefaults.standard

    if let isLogin: Bool = userDefaults.value(forKey:Common.isLoginKey) as! Bool? {
        if (!isLogin) {
            self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LogIn")
        }
   }else {
        self.window?.rootViewController = mainStoryboard.instantiateViewController(withIdentifier: "LogIn")
   }

    return true
}

В SignUpViewController.swift

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

В функции logOutAction

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

Ответ 11

У меня была похожая проблема для решения в приложении, и я использовал следующий метод. Я не использовал уведомления для обработки навигации.

У меня есть три раскадровки в приложении.

  1. Раскадровка на экране-заставке - для инициализации приложения и проверки, вошел ли пользователь в систему.
  2. Раскадровка входа - для обработки потока входа пользователя
  3. Раскадровка панели вкладок - для отображения содержимого приложения

Моя первоначальная раскадровка в приложении - раскадровка экрана-заставки. У меня есть навигационный контроллер в качестве корня раскадровки для входа в систему и панели вкладок для управления навигацией контроллера.

Я создал класс Navigator для обработки навигации в приложении, и он выглядит так:

class Navigator: NSObject {

   static func moveTo(_ destinationViewController: UIViewController, from sourceViewController: UIViewController, transitionStyle: UIModalTransitionStyle? = .crossDissolve, completion: (() -> ())? = nil) {
       

       DispatchQueue.main.async {

           if var topController = UIApplication.shared.keyWindow?.rootViewController {

               while let presentedViewController = topController.presentedViewController {

                   topController = presentedViewController

               }

               
               destinationViewController.modalTransitionStyle = (transitionStyle ?? nil)!

               sourceViewController.present(destinationViewController, animated: true, completion: completion)

           }

       }

   }

}

Давайте посмотрим на возможные сценарии:

  • Первый запуск приложения; Загрузочный экран будет загружен там, где я проверяю, вошел ли пользователь в систему. Затем экран входа в систему будет загружен с использованием класса Navigator следующим образом;

Так как у меня в качестве корневого контроллера навигации, я создаю экземпляр контроллера навигации в качестве начального контроллера представления.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)

Это удаляет раскадровку slpash из корня окна приложения и заменяет ее раскадровкой входа в систему.

Из раскадровки входа в систему, когда пользователь успешно вошел в систему, я сохраняю данные пользователя в User Defaults и инициализирую синглтон UserData для доступа к деталям пользователя. Затем раскадровка панели вкладок загружается методом навигатора.

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

Navigator.moveTo(tabBarNav, from: self)

Теперь пользователь выходит из экрана настроек в панели вкладок. Я очищаю все сохраненные пользовательские данные и перехожу к экрану входа.

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

let loginNav = loginSB.instantiateInitialViewcontroller() as! UINavigationController

Navigator.moveTo(loginNav, from: self)
  • Пользователь вошел в систему и принудительно убивает приложение

Когда пользователь запустит приложение, загрузится заставка. Я проверяю, вошел ли пользователь в систему, и получаю доступ к данным пользователя из настроек пользователя по умолчанию. Затем инициализируйте синглтон UserData и показывает панель вкладок вместо экрана входа в систему.

Ответ 12

Create **LoginViewController** and **TabBarController**.

После создания LoginViewController и TabBarController нам нужно добавить StoryboardID как "loginViewController" и "tabBarController" соответственно.

Тогда я предпочитаю создать структуру Constant:

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

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

В LoginViewController добавьте IBAction:

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

В ProfileViewController добавьте IBAction:

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

В AppDelegate добавьте строку кода в didFinishLaunchingWithOptions:

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

    Switcher.updateRootViewController()

    return true
}

Наконец, создайте класс Switcher:

import UIKit

class Switcher {

    static func updateRootViewController() {

        let status = UserDefaults.standard.bool(forKey: Constants.kUserDefaults.isSignIn)
        var rootViewController : UIViewController?

        #if DEBUG
        print(status)
        #endif

        if (status == true) {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.mainTabBarController) as! MainTabBarController
            rootViewController = mainTabBarController
        } else {
            let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
            let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
            rootViewController = signInViewController
        }

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

    }

}

Это все!

Ответ 13

введите здесь описание изображения

В приложении Delegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
                                                     forBarMetrics:UIBarMetricsDefault];

NSString *identifier;
BOOL isSaved = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginSaved"];
if (isSaved)
{
    //[email protected]"homeViewControllerId";
    UIWindow* mainWindow=[[[UIApplication sharedApplication] delegate] window];
    UITabBarController *tabBarVC =
    [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"TabBarVC"];
    mainWindow.rootViewController=tabBarVC;
}
else
{


    [email protected]"loginViewControllerId";
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:identifier];

    UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:screen];

    self.window.rootViewController = navigationController;
    [self.window makeKeyAndVisible];

}

return YES;

}

view controller.m В поле отображается

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:@selector(logoutButtonClicked:)];
[self.navigationItem setLeftBarButtonItem:barButton];

}

В действии кнопки выхода

-(void)logoutButtonClicked:(id)sender{

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Do you want to logout?" preferredStyle:UIAlertControllerStyleAlert];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Logout" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
           NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:NO forKey:@"loginSaved"];
           [[NSUserDefaults standardUserDefaults] synchronize];
      AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    UIStoryboard *    storyboardobj=[UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *screen = [storyboardobj instantiateViewControllerWithIdentifier:@"loginViewControllerId"];
    [appDelegate.window setRootViewController:screen];
}]];


[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    [self dismissViewControllerAnimated:YES completion:nil];
}]];

dispatch_async(dispatch_get_main_queue(), ^ {
    [self presentViewController:alertController animated:YES completion:nil];
});}