11

Hello, I am writing an app that should respond with an UI update and an internal status change when a local notifcation is used to open it. I am using storyboards and I have set up my main view controller to observe status changes:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumeByNotification:) name:@"Resume" object:nil];
}

In my app delegate I have this:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    if (application.applicationState == UIApplicationStateInactive)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Resume" object:self userInfo:notification.userInfo];
    }
}

And this works just fine: if the app is running in the background, the view controller will intercept the notification and react accordingly. (If the app is running in the foreground, it's ignored because the UI is being taken care of directly.)

The problem arises when the app has been killed and the notification is received. I have written this in the didFinishLaunchingWithOptions method, making the phone vibrate as a quick debug technique :), and I do get the notification:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
    if (localNotification)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Resume" object:self userInfo:localNotification.userInfo];
        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    }

    return YES;
}

The phone DOES vibrate so the notification IS there, but it doesn't seem to trigger the observer. I suppose that this is because the view controller's didViewLoad method hasn't been called yet. I am not sure how to work this around. I suppose I could use UIStoryboard's instantiateViewControllerWithIdentifier: method to make sure the view controller is actually there, but wouldn't I be getting an "extra" instance of it, in addition to the one that will eventually be instantiated by the storyboard's own life cycle? Judging from what the on the class reference documentation says, it's not exactly meant to do this kind of thing.

Am I missing something very obvious here? In fact, is my approach the correct one for this kind of situation?

Thanks!

4

1 回答 1

12

视图控制器不会加载它的视图,直到有东西询问它的视图。在启动时,这通常发生在application:didFinishLaunchingWithOptions:返回后。

你可能想知道为什么。答案是您可能会在启动时实例化多个视图控制器,其中一些是隐藏的。例如,如果您的窗口的根视图控制器是UINavigationController,您可能会加载带有视图控制器堆栈的导航控制器(用户上次在应用程序运行时推送的堆栈)。只有这个堆栈的顶部视图控制器是可见的,所以不需要加载其他视图控制器的视图。系统在加载任何视图之前一直等到application:didFinishLaunchingWithOptions:返回,以便只加载必要的视图。

因此,解决您的问题的一种方法就是向视图控制器询问其视图,从而强制它加载。如果您的视图控制器是窗口的根视图控制器,您可以这样做:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
    if (localNotification) {
        [[self.window rootViewController] view];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Resume" object:self userInfo:localNotification.userInfo];
        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    }

    return YES;
}

另一种解决方法是开始在视图控制器的initWithCoder:方法中观察通知:

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumeByNotification:) name:@"Resume" object:nil];
    }
    return self;
}

当视图控制器从 MainStoryboard 实例化时调用,这发生在application:didFinishLaunchingWithOptions:消息之前。

于 2013-02-10T20:03:56.433 回答