Storyboardlogin屏幕的最佳实践,处理注销时清除数据

我正在构build一个使用Storyboard的iOS应用程序。 根视图控制器是一个选项卡栏控制器。 我正在创buildlogin/注销过程,它大部分工作正常,但我有几个问题。 我需要知道设置所有这一切的最佳方式。

我想完成以下内容:

  1. 首次启动应用时显示login屏幕。 login时,转到选项卡栏控制器的第一个选项卡。
  2. 之后,他们随时启动应用程序,检查它们是否已login,然后直接跳到根标签栏控制器的第一个选项卡。
  3. 当他们手动点击注销button时,显示login屏幕,并清除视图控制器中的所有数据。

到目前为止,我所做的是将根视图控制器设置为Tab Bar Controller,并为我的Login视图控制器创build了一个自定义的Segue。 在我的Tab Bar Controller类中,我检查他们是否在viewDidAppear方法内部login,并执行segue: [self performSegueWithIdentifier:@"pushLogin" sender:self];

我还设置了一个通知,说明何时需要执行注销操作: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];

注销时,我清除Keychain中的证书,运行[self setSelectedIndex:0]并执行segue以再次显示login视图控制器。

这一切工作正常,但我想知道: 这个逻辑应该在AppDelegate? 我也有两个问题:

  • 他们第一次启动应用程序时 ,Tab Bar控制器在执行Segue之前短暂显示。 我试着把代码移动到viewWillAppear但是segue不会那么早。
  • 当他们注销时,所有的数据仍然在所有的视图控制器内。 如果他们login到一个新帐户,旧帐户数据仍然显示,直到他们刷新。 我需要一种方法来清除这个容易注销。

我愿意对此进行修改。 我已经考虑使login屏幕的根视图控制器,或在AppDelegate中创build一个导航控制器来处理一切…我只是不知道什么是最好的方法是在这一点上。

你的故事板应该看起来像这样

在您的didFinishLaunchingWithOptions中的appDelegate.m中

 //authenticatedUser: check from NSUserDefaults User credential if its present then set your navigation flow accordingly if (authenticatedUser) { self.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController]; } else { UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"]; UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController]; self.window.rootViewController = navigation; } 

在SignUpViewController.m文件中

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

在文件MyTabThreeViewController.m

 - (IBAction)actionLogout:(id)sender { // Delete User credential from NSUserDefaults and other data related to user AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate]; UIViewController* rootController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginViewController"]; UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:rootController]; appDelegateTemp.window.rootViewController = navigation; } 

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 

这就是我最终做的一切。 除此之外,唯一需要考虑的是(a)login过程和(b)您存储应用程序数据的位置(在这种情况下,我使用了单例)。

显示登录视图控制器和主选项卡控制器的故事板

正如你所看到的,根视图控制器是我的主选项卡控制器 。 我这样做是因为用户login后,我希望应用程序直接启动到第一个选项卡。 (这可以避免login视图临时显示的任何“闪烁”。)

AppDelegate.m

在这个文件中,我检查用户是否已经login。如果没有,我推动login视图控制器。 我也处理注销过程,我清除数据并显示login视图。

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

在这里,如果login成功,我只是解雇视图并发送通知。

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

编辑:添加注销操作。

在这里输入图像描述

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.创build一个名为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.创build一个新的控制器RootViewController并与第一个视图连接,其中loginbutton在哪里。 添加一个Storyboard ID:“initialView”。

RootViewController.h

 #import <UIKit/UIKit.h> #import "LoginViewController.h" @protocol LoginViewProtocol <NSObject> - (void)dismissAndLoginView; @end @interface RootViewController : UIViewController @property (nonatomic, weak) id <LoginViewProtocol> delegate; @property (nonatomic, retain) LoginViewController *loginView; @end 

RootViewController.m

 #import "RootViewController.h" @interface RootViewController () @end @implementation RootViewController @synthesize loginView; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)loginBtnPressed:(id)sender { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginActionFinished:) name:@"loginActionFinished" object:loginView]; } #pragma mark - Dismissing Delegate Methods -(void) loginActionFinished:(NSNotification*)notification { AppDelegate *authObj = (AppDelegate*)[[UIApplication sharedApplication] delegate]; authObj.authenticated = YES; [self dismissLoginAndShowProfile]; } - (void)dismissLoginAndShowProfile { [self dismissViewControllerAnimated:NO completion:^{ UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UITabBarController *tabView = [storyboard instantiateViewControllerWithIdentifier:@"profileView"]; [self presentViewController:tabView animated:YES completion:nil]; }]; } @end 

4.创build一个新的控制器LoginViewController并与login视图连接。

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中的configuration文件视图连接。

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是一个额外帮助的示例项目。

我不喜欢bhavya的答案,因为在View Controllers中使用AppDelegate并且设置rootViewController没有animation。 Trevor的答案在iOS8上的闪烁视图控制器上有问题。

UPD 07/18/2015

视图控制器中的AppDelegate:

更改视图控制器内的AppDelegate状态(属性)打破封装。

每个iOS项目中对象的非常简单的层次结构:

AppDelegate(拥有windowrootViewController

ViewController(拥有view

顶部的对象可以更改底部的对象,因为它们正在创build它们。 但是如果底层的对象改变了它们之上的对象,那么就不好说了(我介绍了一些基本的编程/ OOP原理:DIP(依赖倒置原则:高层模块不能依赖于低层模块,而应该依赖于抽象) )。

如果有任何对象会改变这个层次结构中的任何对象,迟早会在代码中产生混乱。 在小项目上可能没问题,但在这个项目上挖掘一下这个混乱是没有意义的。

UPD 07/18/2015

我使用UINavigationController复制模态控制器animation(tl; dr:检查项目 )。

我正在使用UINavigationController来呈现我的应用程序中的所有控制器。 最初,我使用普通的push / popanimation在导航堆栈中显示login视图控制器。 比我决定改变它最小的变化模态。

怎么运行的:

  1. 初始视图控制器(或self.window.rootViewController )是具有ProgressViewController作为rootViewController UINavigationController。 我正在显示ProgressViewController,因为DataModel可能需要一些时间来初始化,因为它像本文一样在核心数据栈中(我非常喜欢这种方法)。

  2. AppDelegate负责获取login状态更新。

  3. DataModel处理用户login/注销,AppDelegate通过KVO观察它的userLoggedIn属性。 可以说是不是最好的方法来做到这一点,但它对我有用。 (为什么KVO是坏的,你可以检查这个或这篇文章 (为什么不使用通知?部分)。

  4. ModalDismissAnimator和ModalPresentAnimator用于自定义默认的推动animation。

animation师的逻辑是如何工作的:

  1. AppDelegate将自己设置为self.window.rootViewController (它是UINavigationController)的委托。

  2. 如有必要, -[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]-[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]返回一个animation师。

  3. animation师执行-animateTransition:-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]]; }]; } 

testing项目在这里 。

不build议从应用程序代理执行此操作。 AppDelegatepipe理与启动,挂起,终止等相关的应用程序生命周期。 我build议你从viewDidAppear中的初始视图控制器做到这一点。 您可以从login视图控制器self.presentViewControllerself.dismissViewController 。 将一个bool键存储在NSUserDefaults ,看看它是否第一次启动。

在Xcode 7中,你可以有多个storyBoards。 如果你可以保持loginstream程在一个单独的故事板,会更好。

这可以通过SELECT VIEWCONTROLLER> Editor> Refactor to Storyboard完成

这里是Swift版本,将视图设置为RootViewContoller-

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

这是我的Swifty解决scheme,适合任何未来的围观者。

1)创build一个协议来处理login和注销function:

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

2)扩展所述协议并在此提供用于注销的function:

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

从这里,我的协议扩展将处理逻辑或确定用户是否login/注销,然后相应地更改窗口rootViewController!

我用这个来检查第一次启动:

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

(如果用户删除了应用程序并重新安装,那么就像第一次启动一样)

在AppDelegate中,我检查第一次启动,并创build一个导航控制器,其中包含login屏幕(login和注册),我将其放在当前主窗口的顶部:

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

由于这是常规的视图控制器的顶部,它是独立于您的应用程序的其余部分,你可以closures视图控制器,如果你不再需要它。 而且,如果用户手动按下button,也可以用这种方式呈现视图。

顺便说一句:我保存我的用户login数据是这样的:

 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来pipe理我的数据。 注销只是意味着清空这些数组和字典。 另外我确保在viewWillAppear中设置我的数据。

而已。

我和你的情况一样,我发现清理数据的解决scheme是删除我的视图控制器依赖绘制它的信息的所有CoreData的东西。 但是我仍然发现这种方法非常糟糕,我认为可以在没有故事板的情况下使用代码来pipe理视图控制器之间的转换。

我在Github上发现了这个项目 ,它只是通过代码来完成所有这些工作,而且很容易理解。 他们使用类似Facebook的侧面菜单,他们做的是改变中央视图控制器,取决于用户是否login。 当用户注销appDelegate从CoreData中删除数据并将主视图控制器重新设置为login屏幕。

感谢bhavya的解决scheme。关于迅捷有两个答案,但这些答案并不完整。 我在swift中做了这个。下面是主要的代码。

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

在这里input图像说明

在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; 

}

查看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]; 

}

在注销button操作

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