Tre*_*man 285 login clear logout ios uistoryboard
我正在使用Storyboard构建iOS应用程序.根视图控制器是一个标签栏控制器.我正在创建登录/注销过程,它基本上工作正常,但我有一些问题.我需要知道设置这一切的最好方法.
我想完成以下任务:
到目前为止我所做的是将根视图控制器设置为Tab Bar Controller,并为我的Login视图控制器创建了一个自定义segue.在我的Tab Bar Controller类中,我检查它们是否在viewDidAppear方法内登录,然后执行segue:[self performSegueWithIdentifier:@"pushLogin" sender:self];
我还设置了需要执行注销操作的通知: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutAccount) name:@"logoutAccount" object:nil];
注销后,我清除Keychain中的凭据,运行[self setSelectedIndex:0]并执行segue以再次显示登录视图控制器.
这一切都很好,但我想知道:这个逻辑应该在AppDelegate吗?我还有两个问题:
viewWillAppear但是segue不会那么早.我愿意改变它.我已经考虑将登录屏幕设置为根视图控制器,或者在AppDelegate中创建导航控制器来处理所有事情......我只是不确定此时最好的方法是什么.
bha*_*ari 308

在您的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;
}
Run Code Online (Sandbox Code Playgroud)
在SignUpViewController.m文件中
- (IBAction)actionSignup:(id)sender
{
AppDelegate *appDelegateTemp = [[UIApplication sharedApplication]delegate];
appDelegateTemp.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateInitialViewController];
}
Run Code Online (Sandbox Code Playgroud)
在文件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;
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
在注册视图控制器中:
@IBAction func actionSignup(_ sender: Any) {
let appDelegateTemp = UIApplication.shared.delegate as? AppDelegate
appDelegateTemp?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController()
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
Tre*_*man 97
以下是我最终要做的事情.除此之外,您唯一需要考虑的是(a)登录过程和(b)您存储应用数据的位置(在这种情况下,我使用了单例).

如您所见,根视图控制器是我的主选项卡控制器.我这样做是因为在用户登录后,我希望应用程序直接启动到第一个选项卡.(这可以避免登录视图暂时显示的任何"闪烁".)
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];
}
Run Code Online (Sandbox Code Playgroud)
LoginViewController.m
在这里,如果登录成功,我只是关闭视图并发送通知.
-(void) loginWasSuccessful
{
// Send notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"loginSuccessful" object:self];
// Dismiss login screen
[self dismissViewControllerAnimated:YES completion:nil];
}
Run Code Online (Sandbox Code Playgroud)
Dim*_*kas 20
编辑:添加注销操作.

1.首先准备app委托文件
AppDelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic) BOOL authenticated;
@end
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
2.创建一个名为User的类.
User.h
#import <Foundation/Foundation.h>
@interface User : NSObject
- (void)loginWithUsername:(NSString *)username andPassword:(NSString *)password;
- (void)logout;
- (BOOL)userAuthenticated;
@end
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
3.创建一个新的控制器RootViewController并与第一个视图连接,其中登录按钮处于活动状态.还添加一个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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
4.创建一个新的控制器LoginViewController并与登录视图连接.
LoginViewController.h
#import <UIKit/UIKit.h>
#import "User.h"
@interface LoginViewController : UIViewController
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
5.最后添加一个新的控制器ProfileViewController,并与tabViewController中的配置文件视图相连.
ProfileViewController.h
#import <UIKit/UIKit.h>
@interface ProfileViewController : UIViewController
@end
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
LoginExample是一个示例项目以获得额外帮助.
der*_*iuk 14
我不喜欢bhavya的答案,因为AppDelegate在View Controllers 中使用并且设置rootViewController没有动画.而Trevor的答案与iOS8上的闪存视图控制器有关.
UPD 07/18/2015
视图控制器内部的AppDelegate:
在视图控制器内更改AppDelegate状态(属性)会破坏封装.
每个iOS项目中非常简单的对象层次结构:
AppDelegate(拥有window和rootViewController)
ViewController(拥有view)
顶部的对象可以更改底部的对象,因为它们正在创建它们.但是如果底部的对象改变了它们上面的对象,那就不行了(我描述了一些基本的编程/ OOP原理:DIP(依赖性反转原理:高级模块不能依赖于低级模块,但它们应该依赖于抽象) ).
如果任何对象将更改此层次结构中的任何对象,迟早会在代码中出现问题.在小项目上可能没问题但是在bit projects =]上挖掘这个烂摊子并不好玩
UPD 07/18/2015
我使用UINavigationController(tl; dr:检查项目)复制模态控制器动画.
我正在使用UINavigationController我的应用程序中显示所有控制器.最初,我使用普通的推/动画动画在导航堆栈中显示登录视图控制器.比我决定用最小的改变把它改成模态.
这个怎么运作:
初始视图控制器(或self.window.rootViewController)是带有ProgressViewController的UINavigationController rootViewController.我展示ProgressViewController因为DataModel的可能需要一些时间进行初始化,因为它inits核心数据堆栈像这样的文章(我真的很喜欢这种方法).
AppDelegate负责获取登录状态更新.
DataModel处理用户登录/注销,AppDelegate userLoggedIn通过KVO 观察它的属性.可以说这不是最好的方法,但它对我有用.(为什么KVO不好,您可以查看本文 或本文(为什么不使用通知?部分).
ModalDismissAnimator和ModalPresentAnimator用于自定义默认推送动画.
动画师逻辑的工作原理:
AppDelegate将自己设置为self.window.rootViewController(UINavigationController)的委托 .
-[AppDelegate navigationController:animationControllerForOperation:fromViewController:toViewController:]如有必要,AppDelegate会返回一个动画师.
动画师的实现-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]];
}];
}
Run Code Online (Sandbox Code Playgroud)测试项目在这里.
Har*_*oom 10
这是我未来旁观者的Swifty解决方案.
1)创建一个协议来处理登录和注销功能:
protocol LoginFlowHandler {
func handleLogin(withWindow window: UIWindow?)
func handleLogout(withWindow window: UIWindow?)
}
Run Code Online (Sandbox Code Playgroud)
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()
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
从这里开始,我的协议扩展将处理逻辑或确定用户是否登录/注销,然后相应地更改windows rootViewController!
不建议从应用代理执行此操作.AppDelegate管理与启动,挂起,终止等相关的应用程序生命周期.我建议你从你的初始视图控制器做这个viewDidAppear.你可以self.presentViewController和self.dismissViewController从登录视图控制器.存储bool密钥NSUserDefaults以查看它是否是第一次启动.
在Xcode 7中,您可以拥有多个storyBoards.如果您可以将登录流程保存在单独的故事板中,那将会更好.
这可以使用SELECT VIEWCONTROLLER> Editor> Refactor to Storyboard来完成
这是用于将视图设置为RootViewContoller的Swift版本 -
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window!.rootViewController = newRootViewController
let rootViewController: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController")
Run Code Online (Sandbox Code Playgroud)
After creating the LoginViewController and TabBarController, we need to add a StoryboardID as “loginViewController” and “tabBarController” respectively.
Then I prefer to create the Constant struct:
struct Constants {
struct StoryboardID {
static let signInViewController = "SignInViewController"
static let mainTabBarController = "MainTabBarController"
}
struct kUserDefaults {
static let isSignIn = "isSignIn"
}
}
Run Code Online (Sandbox Code Playgroud)
In LoginViewController add IBAction:
@IBAction func tapSignInButton(_ sender: UIButton) {
UserDefaults.standard.set(true, forKey: Constants.kUserDefaults.isSignIn)
Switcher.updateRootViewController()
}
Run Code Online (Sandbox Code Playgroud)
In ProfileViewController add IBAction:
@IBAction func tapSignOutButton(_ sender: UIButton) {
UserDefaults.standard.set(false, forKey: Constants.kUserDefaults.isSignIn)
Switcher.updateRootViewController()
}
Run Code Online (Sandbox Code Playgroud)
In AppDelegate add line of code in didFinishLaunchingWithOptions:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Switcher.updateRootViewController()
return true
}
Run Code Online (Sandbox Code Playgroud)
Finally create Switcher class:
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
}
}
Run Code Online (Sandbox Code Playgroud)
That is all!
| 归档时间: |
|
| 查看次数: |
112627 次 |
| 最近记录: |