2013-02-10 15 views
11

Ciao, sto scrivendo un app che dovrebbe rispondere con un aggiornamento dell'interfaccia utente e di un cambiamento di stato interno quando una notifica locale viene utilizzato per aprirlo. Sto usando storyboard e ho impostato la mia principale controller della vista per osservare i cambiamenti di stato:applicazione: didFinishLaunchingWithOptions: sparare notifica prima regolatore destinazione viene creata

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

Nella mia app delegato ho questo:

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

E questo funziona bene: se l'applicazione è in esecuzione in background, il controller della vista intercetterà la notifica e reagirà di conseguenza. (Se l'app è in esecuzione in primo piano, viene ignorata perché l'interfaccia utente viene gestita direttamente.)

Il problema sorge quando l'app è stata uccisa e la notifica è stata ricevuta. Ho scritto questo nel metodo didFinishLaunchingWithOptions, facendo vibrare il telefono come una tecnica di debug rapido :), e io fare ottenere la notifica:

- (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; 
} 

il telefono non vibrare la notifica è lì, ma doesn sembra far scattare l'osservatore. Suppongo che ciò sia dovuto al fatto che il metodo viewLoad del controller della vista non è stato ancora chiamato. Non sono sicuro di come farlo. Suppongo di poter usare il metodo instantiateViewControllerWithIdentifier di UIStoryboard: per assicurarsi che il controller della vista sia effettivamente lì, ma non troverei un'istanza "extra" di esso, oltre a quella che verrà eventualmente istanziata dallo stesso ciclo di vita dello storyboard ? A giudicare da ciò che dice la documentazione di riferimento della classe, non è esattamente pensata per fare questo genere di cose.

Mi manca qualcosa di molto ovvio qui? In effetti, il mio approccio è corretto per questo tipo di situazione?

Grazie!

risposta

11

Il controller della vista non carica la sua vista finché qualcosa non chiede la sua vista. Al momento dell'avvio, ciò avviene normalmente dopo i ritorni application:didFinishLaunchingWithOptions:.

Si potrebbe chiedere perché. La risposta è che potresti istanziare diversi controller di visualizzazione al momento del lancio, alcuni dei quali sono nascosti. Ad esempio, se il controller della vista radice della finestra è un UINavigationController, è possibile caricare il controller di navigazione con una pila di controller di visualizzazione (la pila che l'utente aveva premuto l'ultima volta che l'app è stata eseguita). Solo il controller di visualizzazione superiore di questo stack è visibile, quindi non è necessario caricare le viste degli altri controller di visualizzazione. Il sistema attende i ritorni application:didFinishLaunchingWithOptions: prima di caricare qualsiasi vista in modo da caricare solo le viste necessarie.

Quindi, un modo per aggirare il problema è semplicemente chiedere al controller della vista di visualizzarlo, forzandolo quindi a caricarlo. Se il controller di vista è controller della vista radice della finestra, si può fare in questo modo:

- (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; 
} 

Una soluzione diversa potrebbe essere quella di iniziare a osservare la notifica nella initWithCoder: il metodo del controller della vista:

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

Questo viene chiamato quando il controller di visualizzazione viene istanziato dalla MainStoryboard, cosa che avviene prima del messaggio application:didFinishLaunchingWithOptions:.

+0

Grazie, usando 'initWithcoder:' ha funzionato come un incantesimo. Tutti questi passaggi nel ciclo di vita di un oggetto possono diventare piuttosto confusi. :) – Jollino

+0

Grazie, questo funziona anche se stai usando le notifiche push invece delle notifiche locali e non lo storyboard. Basta usare i metodi 'init'. – yoeriboven

+0

['init (coder:)'] (https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/index.html#//apple_ref/occ/intfm/NSCoding/initWithCoder :) –