草庐IT

ios - 在 iOS 上创建一个始终显示在所有 View 顶部的登录屏幕

coder 2024-01-20 原文

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

我正在为我的应用程序设计一个登录屏幕,除了少数极端情况外,我大部分时间都在使用它。我已经进行了设置,以便我在应用程序委托(delegate) applicationDidBecomeActive: 方法中触发的 Storyboard中的 UITabBar 中有一个 segue。正如我所说,它在除我目前发现的一种边缘情况外的所有情况下都能正常工作。

我的应用程序使用一些模态视图 Controller (其中一些是 UIActivityViewControllers,如果有区别的话)来输入和编辑一些核心数据实体。如果在应用程序进入后台时打开这些模态视图 Controller 之一,它会在应用程序重新打开时始终显示,而我的登录名不会显示。我收到以下控制台消息

Warning: Attempt to present <UINavigationController: 0x1d51e320>  on <MPTabBarViewController: 0x1d5b4810> which is already presenting <UIActivityViewController: 0x1e38fc40>

这是我的代码

   - (void) displayLogin{
        NSLog(@"%s", __PRETTY_FUNCTION__);

        UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;

        NSDate *lastDate = [[NSUserDefaults standardUserDefaults] objectForKey:MPLastCloseDate];
        NSTimeInterval timeDiff = [[NSDate date] timeIntervalSinceDate:lastDate];
        int seconds = timeDiff;

        if ([[NSUserDefaults standardUserDefaults] integerForKey:MPPassCodeDelay] == MPScreenLockAlways || seconds >= 300) {
            NSLog(@"Should see login");
            [tabBarController performSegueWithIdentifier:@"loginScreen" sender:self];
        }
    }

我完全理解这条消息告诉我的意思,标签栏已经呈现了一个模态 Controller ,所以它不能呈现另一个。所以我的问题是,是否有更好的方法来实现这一点,以便登录始终显示,甚至在模态视图之上?


好的,这是我目前的解决方案

根据 Bartu 的建议并要求由 Shawn 分享

我有一个工作的单例 loginManager 类,它需要在应用程序委托(delegate)中进行 1 次调用,并在任何可以调用以呈现为模态的 View Controller 中进行 1 次调用。我无法按照 ViewController 类别的建议弄清楚如何执行此操作,但是嘿,一些包含和方法调用还不错。我将它包含在 App-Prefix.pch 中,因此它随处可用。它是为 ARC 编写的,所以如果你喜欢管理自己的内存,你需要为此修改单例。最后一个警告,目前您需要为登录屏幕滚动自己的 viewController。只需在带有所有星星的实现中查找注释部分,然后将您自己的 View Controller 放在那里。我的仍然在我的应用程序 Storyboard 中,它基本上是 4 位数字 pin,用于检查钥匙串(keychain)中的匹配项并为正确的 pin 自行解雇。我可能会把它从我的 Storyboard 中拉出来并 nib 它,这样它就可以与 loginManager 打包在一起,并让它在未来的某个日期成为我的第一个 gitHub 项目。

您可以将其配置为在每次打开应用程序时或在延迟属性后显示登录信息。延迟时间也是一个以秒为单位设置的属性。它还会在几秒钟内阻止您的应用程序 UI,以便使用您的应用程序 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];
}

登录管理器类

标题:

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

最佳答案

不久前我遇到了同样的问题,我对这个问题的解决方案是引用您的应用委托(delegate)中当前呈现的任何模态视图。因此,您可以知道您的选项卡栏 Controller 是否已经呈现模态 Controller ,如果是这种情况,您可以在当前模态视图上呈现登录 View 。

关于ios - 在 iOS 上创建一个始终显示在所有 View 顶部的登录屏幕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16839171/

有关ios - 在 iOS 上创建一个始终显示在所有 View 顶部的登录屏幕的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  4. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  7. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  8. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  9. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  10. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

随机推荐