我希望在我的应用启动时呈现模态视图控制器(用于登录屏幕),以及在用户点击主页按钮然后重新启动应用后再次变为活动状态时。
我首先尝试在根视图控制器的viewDidAppear:
方法中呈现模态视图。应用程序首次启动时效果很好,但是当应用再次变为活动状态时,不会调用此方法。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self presentModalView];
}
- (void)presentModalView {
if(![AuthenticationService sharedInstance].isAuthenticated) {
_modalVC = [self.storyboard instantiateViewControllerWithIdentifier:self.modalViewControllerIdentifier];
_modalVC.delegate = self;
[self presentViewController:_modalVC animated:YES completion:nil];
}
}
接下来,我尝试使用applicationDidBecomeActive:
方法从我的应用代理中调用此内容。
- (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.
ModalPresentingUISplitViewController *splitViewController = (ModalPresentingUISplitViewController *)self.window.rootViewController;
[splitViewController presentModalView];
}
这似乎在表面上工作正常,但我在日志中收到Unbalanced calls to begin/end appearance transitions for <ModalPresentingUISplitViewController: 0x7251590>
警告。我觉得我在UISplitView完成呈现之前会以某种方式呈现模态视图,但我不知道如何解决这个问题。
当应用程序变为活动状态时,如何从根视图控制器“自动”显示模态视图,并在“正确”时刻执行此操作,以免不平衡我的分割视图控制器?
答案 0 :(得分:1)
忘了这个问题在这里。是的,我有一个解决方案。我不禁觉得有更优雅或正确的方式来做到这一点,但这对我有用......
这假设您使用ARC和故事板;您已经为登录视图创建了一个UIViewController,其中包含来自UISplitViewController的模态segue(或者您的根视图控制器)。
UISplitViewController(或任何你的根视图控制器)
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentModalView) name:UIApplicationDidBecomeActiveNotification object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.viewHasAppeared = YES;
[self presentModalView];
}
- (void) presentModalView {
if(self.viewHasAppeared && !self.userAuthenticated) {
[self performSegueWithIdentifier:@"ShowLoginView" sender:self];
}
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:@"ShowLoginView"]) {
JDPLoginViewController *dest = [segue destinationViewController];
dest.delegate = self;
}
}
- (void) dismissLogin {
self.userAuthenticated = YES;
[self dismissViewControllerAnimated:YES completion:nil];
}
以下是要注意的代码的重要部分......
presentModalView
两个地方 - viewDidAppear
,这将在应用首次启动时显示我们的登录视图,并且presentModalView
作为UIApplicationDidBecomeActiveNotification事件的观察者,因此当应用程序在后台运行后变为活动状态时会调用该方法。viewHasAppeared
,以跟踪UISplitViewController的视图是否出现,因此我们不会尝试在UISplitViewController的视图出现之前呈现模态登录。 以下是不同的方案......
App First Starts:
presentModalView
由UIApplicationDidBecomeActiveNotification
事件调用,但由于未加载UISplitViewController的视图(并且viewHasAppeared
BOOL为NO,因此没有任何反应.Win。我们不存在我们不应该这样看。
viewDidAppear
,将viewHasAppeared
设置为YES,然后调用presentModalView
。出现登录屏幕。一切都按预期工作 - 耶!应用程序在后台运行后变为活动
presentModalView
由UIApplicationDidBecomeActiveNotification
事件再次调用,如第一个场景中所示,但这次viewHasAppeared
为YES,因此登录视图按预期显示。再说一遍!
就像我说的那样,这感觉很难看,但是直到找到更好的解决方案才能完成工作。希望它适合你。
答案 1 :(得分:0)
您是否尝试过UIView的viewWillAppear?
答案 2 :(得分:0)
关闭@ jpolete的答案我做的事情有点不同。此外,我希望登录屏幕仅在应用程序在后台运行15秒后才会显示(用户始终必须重新登录才会感到痛苦)。
此演示的源代码可在github
上找到与@jpolete一样,我将大部分逻辑封装在根视图控制器中,在我的情况下是一个导航控制器(iPhone示例)。 userLoggedIn
标记用户是否已经过身份验证。 presentingLoginController
标志让我知道当前是否显示登录屏幕。 backgroundTime
包含用户进入后台时的时间戳。这是类扩展:
@interface RootNavigationController () <LoginDelegate>
@property (assign, nonatomic) BOOL userLoggedIn;
@property (strong, nonatomic) NSDate *backgroundTime;
@property (assign, nonatomic) BOOL presentingLoginController;
-(void)applicationDidBecomeActive:(NSNotification*) notification;
-(void)applicationDidEnterBackground:(NSNotification*) notification;
@end
加载视图后,我添加了相应的通知挂钩:
@implementation RootNavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
如果用户未经过身份验证且我们当前未提供登录控制器,则会触发登录segue。
-(void)loginIfNecessary {
if (!self.userLoggedIn && !self.presentingLoginController) {
self.presentingLoginController = YES;
[self performSegueWithIdentifier:@"RootLoginSegue" sender:self];
}
}
这里我们将根视图控制器设置为登录控制器的loginDelegate
。
成功登录时会通知此代理(登录控制器嵌入在另一个导航控制器中):
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"RootLoginSegue"]) {
UINavigationController *navController = segue.destinationViewController;
LoginTableViewController *loginController = (LoginTableViewController *) navController.topViewController;
loginController.loginDelegate = self;
}
}
成功登录后,我们会执行以下操作:
-(void)didLogin { // LoginDelegate method called to login controller after successsful login
self.presentingLoginController = NO;
self.userLoggedIn = YES;
}
当视图第一次出现时,当它出现在后台后,或者在被“掩盖”后,我们登录(如果需要):
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self loginIfNecessary];
}
当我们进入背景时,我们记录时间:
-(void)applicationDidEnterBackground:(NSNotification*) notification {
self.backgroundTime = [NSDate date];
}
当我们进入前景并且它第一次或足够的时间过去了然后我们 强制用户再次登录(如有必要):
-(void) applicationDidBecomeActive:(NSNotification*) notification {
const NSTimeInterval maxBackgroundTime = 15.0;
if (!self.backgroundTime || [[NSDate date] timeIntervalSinceDate:self.backgroundTime] > maxBackgroundTime) {
self.userLoggedIn = NO;
}
[self loginIfNecessary];
}
@end