在iOS上创建一个登录屏幕,该屏幕将始终显示在所有视图的顶部

在iOS上创建一个登录屏幕,该屏幕将始终显示在所有视图的顶部,ios,login,modalviewcontroller,Ios,Login,Modalviewcontroller,编辑:检查下面的解决方案 我正在为我的应用程序设计一个登录屏幕,除少数边缘情况外,我的应用程序大部分都能正常工作。我已经设置好了,这样我就可以在故事板中有一个与我的UITabBar的片段,我可以在app delegate applicationIDbecomeactive:method中触发它。正如我所说的,目前为止,除了我发现的一个边缘情况外,它在所有情况下都能正常工作 我的应用程序使用一些模式视图控制器(如果有区别的话,其中一些是UIActivityViewController)来输入和编辑

编辑:检查下面的解决方案

我正在为我的应用程序设计一个登录屏幕,除少数边缘情况外,我的应用程序大部分都能正常工作。我已经设置好了,这样我就可以在故事板中有一个与我的UITabBar的片段,我可以在app delegate applicationIDbecomeactive:method中触发它。正如我所说的,目前为止,除了我发现的一个边缘情况外,它在所有情况下都能正常工作

我的应用程序使用一些模式视图控制器(如果有区别的话,其中一些是UIActivityViewController)来输入和编辑一些核心数据实体。如果在应用程序进入后台时打开其中一个模式视图控制器,则当应用程序重新打开且我的登录名不显示时,它将始终显示。我得到以下控制台消息

Warning: Attempt to present <UINavigationController: 0x1d51e320>  on <MPTabBarViewController: 0x1d5b4810> which is already presenting <UIActivityViewController: 0x1e38fc40>
我完全理解这个消息告诉我的,标签栏已经显示了一个模态控制器,所以它不能显示另一个。所以我的问题是,有没有更好的方法来实现这一点,以便登录始终显示,甚至在模式视图之上


好的,这是我当前的解决方案 根据巴图的建议,并要求Shawn分享

我有一个正在工作的singleton loginManager类,它需要在应用程序委托中调用1个,在任何视图控制器中调用1个,这些视图控制器可以被调用以显示为模态。我不知道如何按照ViewController类别的建议执行此操作,但有几个include和方法调用并没有那么糟糕。我把它包括在App-Prefix.pch中,所以它随处可见。它是为ARC编写的,因此如果您喜欢管理自己的内存,则需要为此修改单例。最后一个警告是,目前您需要为登录屏幕滚动您自己的viewController。只需在实现中查找带有所有星星的注释部分,并在那里放置您自己的视图控制器。我的仍然在我的应用程序情节串连图板中,它基本上是4位pin码,用于检查钥匙链中是否匹配,并自动删除以获得正确的pin码。我可能会把它从我的故事板中拉出来,然后对它进行修改,这样它就可以和loginManager一起打包,并在将来某个时候让它成为我的第一个gitHub项目

您可以将其配置为在每次打开应用程序时或延迟一段时间后使用属性显示登录信息。延迟时间也是以秒为单位设置的属性。它还会在几秒钟内屏蔽你的应用程序界面,以便使用你的apps Default.png以飞溅的方式显示登录。这也可以通过属性进行配置

我很想得到一些关于这方面的反馈,如果有人能告诉我如何做一个类别,这样就不需要在viewControllers中进行额外的调用,那就太好了!享受吧

AppDelegate:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    [self.window makeKeyAndVisible];

    // these calls are all optional
    [[VHLoginManager loginManager] setShouldBlockUIWithSplashOnResume:NO];
    [[VHLoginManager loginManager] setSecondsRequiredToPassBeforeLockDown:1000];
    [[VHLoginManager loginManager] setScreenLockRequirment:VHLMScreenLockDelayed];

    // this is the only required call to run with defaults - always login and block UI with splash while login loads
    [[VHLoginManager loginManager] presentLogin];
}
可能在某个点显示为模态的任何viewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[VHLoginManager loginManager] registerViewControllerIfModal:self];
}
loginManager类

标题:

//  VHLoginManager.h
//  Created by Victor Hudson on 5/31/13.
//  Copyright (c) 2013 Victor Hudson. All rights reserved.
//  Use if you like but be nice and leave my name 

#import <Foundation/Foundation.h>
#define VHLMLastCloseDate @"VHLMLastCloseDate"
#define VHLMPassCodeDelay @"VHLMPassCodeDelay"

typedef enum {
    VHLMScreenLockAlways = 0,
    VHLMScreenLockDelayed = 1,
} VHLMScreenLockRequirement;

@interface VHLoginManager : NSObject
@property (nonatomic) BOOL shouldBlockUIWithSplashOnResume;
// defaults to yes so app contents arent visible before the login screen appears
@property (nonatomic) int secondsRequiredToPassBeforeLockDown;
// defaults to 5 minutes (300)

#pragma mark - Class Methods
+ (VHLoginManager *)loginManager;
// returns the singleton login manager

#pragma mark - Manager Methods
- (void) presentLogin;
// will determine if login should be presented an do so if needed
- (void) registerViewControllerIfModal:(UIViewController *)controller;
// any view controllers that are presented modally should call this with self as controller in viewDidLoad - the pupose of this manager is so login shows even over top of modals
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement;
// deafaults to always if not adjusted
@end

我所做的是在appDelegate中设置一个开关。当应用程序启动时,如果用户没有登录,我创建了登录视图并将其设置为窗口的rootViewController。当用户成功登录时,我使用动画块将该视图的alpha设置为0,然后创建一个UITabBarController,填充它,使其成为窗口的rootViewController(alpha为0,然后将其alpha设置为1)。工作真的很好。但我不知道如何使用故事板来实现这一点

编辑:现在开始熟悉故事板。因此,您不需要使用Main.storyboard本身(将其从info.plist中删除),然后添加一个LoginViewController作为视图,并将UITabbarController也放在那里—但没有任何内容是初始视图控制器。显然,您必须命名每个视图,以便在代码中创建它,但要求情节提要创建这样或那样的视图控制器


因此,在应用程序内委托,如果登录,则实例化选项卡栏控制器并将其添加为根视图控制器。如果用户尚未登录,请创建LoginView并将其添加为rootview控制器。如果用户确实登录,请在LoginViewController上使用某种方法,以便它可以要求代理切换到选项卡栏控制器。

我不久前也遇到过同样的问题,我的解决方案是引用应用程序代理中当前显示的任何模式视图。因此,您可以知道您的选项卡栏控制器是否已经显示模式控制器,如果是这种情况,您可以在当前模式视图上显示登录视图。

这听起来正是我需要的。谢谢如果他们只是监听代理消息通知并响应它们,而不是appDelegate对modals的引用,这会更像MVC吗?听起来更像MVC:)甚至可以创建一个singleton来监听通知并管理登录视图的显示。这也将使您的登录视图控制器的机制可移植。因此,要做到这一点,singleton类必须跟踪打开的模式视图,然后根据通知触发它们,对吗?原谅我。我不是一个完全的新手,但仍然是一个新手;-)如果你这样做,如果你能发布你的代码,那将是非常好的!我还是个新手,一个真正的MVC解决方案可能很有启发性。谢谢。太棒了,其他所有的答案都是使用modal,但是这个效果非常好+1@RobR.,使用Xcode 5中的故事板,我编辑了答案,以提供如何使用故事板进行回答的指导。
//  VHLoginManager.h
//  Created by Victor Hudson on 5/31/13.
//  Copyright (c) 2013 Victor Hudson. All rights reserved.
//  Use if you like but be nice and leave my name 

#import <Foundation/Foundation.h>
#define VHLMLastCloseDate @"VHLMLastCloseDate"
#define VHLMPassCodeDelay @"VHLMPassCodeDelay"

typedef enum {
    VHLMScreenLockAlways = 0,
    VHLMScreenLockDelayed = 1,
} VHLMScreenLockRequirement;

@interface VHLoginManager : NSObject
@property (nonatomic) BOOL shouldBlockUIWithSplashOnResume;
// defaults to yes so app contents arent visible before the login screen appears
@property (nonatomic) int secondsRequiredToPassBeforeLockDown;
// defaults to 5 minutes (300)

#pragma mark - Class Methods
+ (VHLoginManager *)loginManager;
// returns the singleton login manager

#pragma mark - Manager Methods
- (void) presentLogin;
// will determine if login should be presented an do so if needed
- (void) registerViewControllerIfModal:(UIViewController *)controller;
// any view controllers that are presented modally should call this with self as controller in viewDidLoad - the pupose of this manager is so login shows even over top of modals
- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement;
// deafaults to always if not adjusted
@end
//  VHLoginManager.m
//  Created by Victor Hudson on 5/31/13.
//  Copyright (c) 2013 Victor Hudson. All rights reserved.
//  Use if you like but be nice and leave my name

#import "VHLoginManager.h"
static VHLoginManager *loginManager = nil;

@interface VHLoginManager ()
@property (nonatomic, strong) UIViewController *currentModalViewController;
@property (nonatomic) VHLMScreenLockRequirement screenLockrequirement;
@end

@implementation VHLoginManager
#pragma mark - Manager Methods
- (void) presentLogin
{
    // NSLog(@"%s", __PRETTY_FUNCTION__);
    if ([[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay] == VHLMScreenLockAlways || [self timeSinceLastClose] >= self.secondsRequiredToPassBeforeLockDown) {
        //NSLog(@"User should see login");
        // determine who the presenting view controller should be
        UIViewController *viewController;
        if (self.currentModalViewController && self.currentModalViewController.presentingViewController != nil) {
            // NSLog(@"We have a modal view controller on top");
            viewController = self.currentModalViewController;
        } else {
            // NSLog(@"We have NO modal view controller on top");
            // get the root view controller of the app
            viewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
        }

//********************************************************************************************************************************************************************************
        // *** This is still tied into my app storyboard and should be made into a viewcontroller with nib to be portable with loginManager for now implement and present your own loginViewController
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];
        UINavigationController *navController = [storyboard instantiateViewControllerWithIdentifier:@"appLoginScreen"];
//********************************************************************************************************************************************************************************

        // present the login to user
        [viewController presentViewController:navController animated:NO completion:nil];
    }
}

- (void) setScreenLockRequirment:(VHLMScreenLockRequirement) requirement
{
    _screenLockrequirement = requirement;
    [[NSUserDefaults standardUserDefaults] setInteger:self.screenLockrequirement forKey:VHLMPassCodeDelay];
}

- (void) registerViewControllerIfModal:(UIViewController *)controller
{
    // NSLog(@"%s", __PRETTY_FUNCTION__);
    if (controller.presentingViewController) {
        NSLog(@"Registering a modalViewController");
        self.currentModalViewController = controller;
    }
}

#pragma mark - Private Methods

- (void) timeStampForBackground
{
    // NSLog(@"%s", __PRETTY_FUNCTION__);
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:VHLMLastCloseDate];
    [self setDisplaySplashForBackgroundResume];
}

- (int) timeSinceLastClose
{
    return [[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:VHLMLastCloseDate]];
}

#pragma mark Splash Screen management
- (void) setDisplaySplashForBackgroundResume
{
    // NSLog(@"%s", __PRETTY_FUNCTION__);
    if (self.shouldBlockUIWithSplashOnResume) {
        // dismiss all keyboards and input views
        UIView *topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
        [topView endEditing:YES];

        // Don't show a splash screen if the application is in UIApplicationStateInactive (lock/power button press)
        UIApplication *application = [UIApplication sharedApplication];
        if (application.applicationState == UIApplicationStateBackground) {
            UIImageView *splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Default"]];
            splash.frame = application.keyWindow.bounds;
            [application.keyWindow addSubview:splash];
        }
    }
}

- (void) removeSplashScreen
{
    // NSLog(@"%s", __PRETTY_FUNCTION__);
    if (self.shouldBlockUIWithSplashOnResume) { // we should have a splash image up if true
                                                // so remove it
        UIWindow *thewindow = [[UIApplication sharedApplication] keyWindow];
        if ([[thewindow subviews] count] > 1) {
            [NSThread sleepForTimeInterval:1.0];
            [[[thewindow subviews] lastObject] removeFromSuperview];
        }
    }
}
#pragma mark - Class Management

//prevent additional instances
+ (id)allocWithZone:(NSZone *)zone
{
    return [self loginManager];
}

+ (VHLoginManager *)loginManager
{
    if (!loginManager) {
        //Create The singleton
        loginManager = [[super allocWithZone:NULL] init];
    }

    return loginManager;
}

- (id) init
{
    // If we already have an instance of loginManager
    if (loginManager) {
        //Return The Old One
        return loginManager;
    }

    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(timeStampForBackground)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(removeSplashScreen)
                                                     name:UIApplicationDidBecomeActiveNotification
                                                   object:nil];
        self.shouldBlockUIWithSplashOnResume = YES;
        self.secondsRequiredToPassBeforeLockDown = 300;

        if (![[NSUserDefaults standardUserDefaults] integerForKey:VHLMPassCodeDelay]) {
            [self setScreenLockRequirment:VHLMScreenLockAlways];
        }
    }
    return self;
}
@end