wake-up-neo.com

Testen Sie, ob die App von einer UILocalNotification aus aktiv wurde

Gibt es eine Möglichkeit zu erfahren, ob die Anwendung durch eine lokale Benachrichtigung aktiv wurde? 

Ich weiß, dass es eine Möglichkeit gibt, zu testen, ob die Anwendung gestartet aus einer lokalen Benachrichtigungswarnung war. aber wenn da draußen nur der Hintergrund saß und eine Benachrichtigung erhalten wurde?

Ich muss anderen Code ausführen, wenn die App aktiv geworden ist:

  1. Aus einer lokalen Benachrichtigung. 
  2. Ist gerade aktiv geworden :)

Gibt es eine Möglichkeit, dies zu tun?

43
wh1t3cat1k

Ich fürchte, Sylter ist falsch. Wenn eine App in den Vordergrund aus dem Hintergrund gelangt, entweder durch eine direkte Benutzeraktion oder durch eine Benutzerantwort auf eine UILocalNotification, wird applicationDidFinishLaunchingWithOptions nicht ausgelöst. Es werden jedoch applicationWillEnterForeground und applicationDidBecomeActive aufgerufen. Dies kann mit ein paar NSLogs überprüft werden.

Das Problem bleibt also bestehen: Wenn eine App aus dem Hintergrund in den Vordergrund gelangt, gibt es keine Möglichkeit herauszufinden, ob die App als Reaktion auf die Antwort eines Benutzers auf eine UILocalNotification in den Vordergrund eintritt oder ob sie nur in den Vordergrund gelangt. Außer...

Sobald die App in den Vordergrund eingetreten ist, erhält sie die Methode application:DidReceiveLocalNotification: wenn die App als Reaktion auf eine UILocalNotification den Vordergrund eingegeben hat.

Das Problem ist, dass alle Änderungen an der Benutzeroberfläche, die innerhalb der application:DidReceiveLocalNotification:-Methode als Antwort auf den Erhalt der UILocalNotification vorgenommen wurden, auftreten nachdem die App bereits in den Vordergrund eingetreten ist, was für den Benutzer eine störende Erfahrung darstellt.

Hat jemand eine Lösung gefunden?

29
jaredsinclair

Ich habe den Hinweis auf die Lösung dafür von @ naveed's Tipp erhalten, wie der Status der Anwendung überprüft wird, wenn die Methode didReceiveNotification aufgerufen wird.

Unter iOS7 und niedriger Sie behandeln die Benachrichtigungen wie folgt:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    if (application.applicationState == UIApplicationStateInactive ) {
         //The application received the notification from an inactive state, i.e. the user tapped the "View" button for the alert.
         //If the visible view controller in your view controller stack isn't the one you need then show the right one.
    }

    if(application.applicationState == UIApplicationStateActive ) { 
        //The application received a notification in the active state, so you can display an alert view or do something appropriate.
    }
}

Update für iOS 8: Die folgenden Methoden werden jetzt aufgerufen, wenn die App über eine Benachrichtigung aus dem Hintergrund geöffnet wird.

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler {
}

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler {
}

Wenn die Benachrichtigungen empfangen werden, während sich die App im Vordergrund befindet, verwenden Sie die folgenden Methoden:

- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *) userInfo {
}

- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
}

Beachten Sie, dass Sie den Anwendungsstatus nicht überprüfen müssen, es sei denn, Sie möchten ältere Versionen des Betriebssystems in Ihrer App unterstützen.

63
Michael Gaylord

Sie können die Szenarien überprüfen, in denen eine Anwendung ausgeführt wird oder nicht, wenn die Anwendung empfangen wurde.

- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
    if (app.applicationState == UIApplicationStateInactive ) {
        NSLog(@"app not running");
    }else if(app.applicationState == UIApplicationStateActive )  {
        NSLog(@"app running");      
    }

    // Handle the notificaton when the app is running
    NSLog(@"Recieved Notification %@",notif);
}
10
naveed

Ich habe zwei Szenarien ausprobiert. Zum einen, um die App wieder in den Vordergrund zu stellen, indem Sie auf das Symbol klicken, ein anderes per URL-Sys-Aufruf, und alle Variablen in UIApplication verglichen. Überraschenderweise fand ich schließlich das, wonach ich suchte in UIApplication.h:

struct {
    unsigned int isActive:1;
    unsigned int isSuspended:1;
    unsigned int isSuspendedEventsOnly:1;
    unsigned int isLaunchedSuspended:1;
    unsigned int calledNonSuspendedLaunchDelegate:1;
    unsigned int isHandlingURL:1;
    unsigned int isHandlingRemoteNotification:1;
    unsigned int isHandlingLocalNotification:1;
    unsigned int statusBarShowsProgress:1;
    unsigned int statusBarRequestedStyle:4;
    unsigned int statusBarHidden:1;
    unsigned int blockInteractionEvents:4;
    unsigned int receivesMemoryWarnings:1;
    unsigned int showingProgress:1;
    unsigned int receivesPowerMessages:1;
    unsigned int launchEventReceived:1;
    unsigned int isAnimatingSuspensionOrResumption:1;
    unsigned int isResuming:1;
    unsigned int isSuspendedUnderLock:1;
    unsigned int isRunningInTaskSwitcher:1;
    unsigned int shouldExitAfterSendSuspend:1;
    unsigned int shouldExitAfterTaskCompletion:1;
    unsigned int terminating:1;
    unsigned int isHandlingShortCutURL:1;
    unsigned int idleTimerDisabled:1;
    unsigned int deviceOrientation:3;
    unsigned int delegateShouldBeReleasedUponSet:1;
    unsigned int delegateHandleOpenURL:1;
    unsigned int delegateOpenURL:1;
    unsigned int delegateDidReceiveMemoryWarning:1;
    unsigned int delegateWillTerminate:1;
    unsigned int delegateSignificantTimeChange:1;
    unsigned int delegateWillChangeInterfaceOrientation:1;
    unsigned int delegateDidChangeInterfaceOrientation:1;
    unsigned int delegateWillChangeStatusBarFrame:1;
    unsigned int delegateDidChangeStatusBarFrame:1;
    unsigned int delegateDeviceAccelerated:1;
    unsigned int delegateDeviceChangedOrientation:1;
    unsigned int delegateDidBecomeActive:1;
    unsigned int delegateWillResignActive:1;
    unsigned int delegateDidEnterBackground:1;
    unsigned int delegateWillEnterForeground:1;
    unsigned int delegateWillSuspend:1;
    unsigned int delegateDidResume:1;
    unsigned int userDefaultsSyncDisabled:1;
    unsigned int headsetButtonClickCount:4;
    unsigned int isHeadsetButtonDown:1;
    unsigned int isFastForwardActive:1;
    unsigned int isRewindActive:1;
    unsigned int disableViewGroupOpacity:1;
    unsigned int disableViewEdgeAntialiasing:1;
    unsigned int shakeToEdit:1;
    unsigned int isClassic:1;
    unsigned int zoomInClassicMode:1;
    unsigned int ignoreHeadsetClicks:1;
    unsigned int touchRotationDisabled:1;
    unsigned int taskSuspendingUnsupported:1;
    unsigned int isUnitTests:1;
    unsigned int requiresHighResolution:1;
    unsigned int disableViewContentScaling:1;
    unsigned int singleUseLaunchOrientation:3;
    unsigned int defaultInterfaceOrientation:3;
} _applicationFlags;

Diese enthält möglicherweise alle Informationen, auf die ein Programmierer Zugriff haben möchte, wenn die Anwendung in den Vordergrund zurückkehrt. Insbesondere möchte ich auf das Flag "isHandlingURL" zugreifen, das den Wert 1 angibt, wenn die Anwendung durch ein System in den Vordergrund gestellt wird. call, 0, wenn die App vom Benutzer in den Vordergrund gestellt wird.

Als nächstes schaute ich mir die Adresse von "application" und "_applicationFlags" an und bemerkte, dass sie um 0x3C (60) versetzt sind. Daher entschied ich mich, Adressoperationen zu verwenden, um mein benötigtes Bit zu erhalten:

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
     */
    id* app = [UIApplication sharedApplication];
    app = app+15; //address increments by long words, don't know if it will be the same on device
    NSLog(@"Test:%x",*app);
}

die test ausdrückt: 4a40012 oder 0x04a40012, wenn ich in einem vollständigen langen Word-Format schreibe .. _. Dies gibt mir binär 0000 0100 1010 0100 0000 0000 0001 0010 ..__ Wenn Sie sich wieder _applicationFlags ansehen, erhalten Sie "isHandlingURL" für das 6. Bit von LSB (0). Wenn ich nun die App in den Hintergrund bringe und sie mit einer sys-URL zurückbringe, erhalte ich einen Ausdruck von 4a40032 mit Binärzeichen 0000 0100 1010 0100 0000 0000 0011 0010 und ich habe mein isHandlingURL-Bit eingeschaltet! Es bleibt also nur noch die Anweisung durch Bit-Shift-Operationen zu vervollständigen. Der endgültige Code sieht dann so aus:

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
     */
    id* app = (id*)[UIApplication sharedApplication]+15;
    BOOL isHandlingURL = ((Byte)*app>>5&0x1);
    if (isHandlingURL) {
        //do whatever I wanna do here
    }
}

Ich kann fortfahren und eine vollständige Funktion schreiben, um alle _applicationFlag zu analysieren, aber dann ist es an diesem Punkt nicht sicher, ob das Adressinkrement sowohl auf dem Simulator als auch auf dem Ziel 15 festgelegt ist. Mein nächstes Ziel wird es sein, durch Magie zu ersetzen Nummer '15' durch einige Makros definiert oder Werte aus dem System, so dass ich sicher bin, dass es immer 0x3C nach Bedarf verschieben wird, und ich muss in den UIApplication-Header schauen, um sicherzustellen, dass das _applicationFlag immer um 0x3C verschoben wird.

Das ist alles für jetzt!

7
Paul

In Ihrer AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    // Override point for customization after application launch.

    UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

    if (localNotif) {
        NSLog(@"Recieved Notification %@",localNotif);
    //Do Something
    } else {
    //Do Something else if I didn't recieve any notification, i.e. The app has become active
    }

    return YES;
}

Wenn Sie wissen möchten, wann sich die Anwendung im Vordergrund oder im Hintergrund befindet, können Sie diese Methode verwenden:

- (void)applicationWillResignActive:(UIApplication *)application {
    /*
     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
     */
} 

- (void)applicationDidEnterBackground:(UIApplication *)application {
    /*
     Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
     If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
     */
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    /*
     Called as part of  transition from the background to the active state: here you can undo many of the changes made on entering the background.
     */
}


- (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.
     */
}


- (void)applicationWillTerminate:(UIApplication *)application {
    /*
     Called when the application is about to terminate.
     See also applicationDidEnterBackground:.
     */
}
4
Sylter

Um mehr Licht in das Problem zu bringen, habe ich gerade getestet, wie ich meine App von einer lokalen Benachrichtigung aus startete und die Reihenfolge beobachtete, in der App-Delegat-Methoden aufgerufen wurden. Meine Testgeräte waren ein iPod Touch der 5. Generation mit iOS 7.1.1 und ein iPhone 4S mit iOS 7.1.1. Die Reihenfolge der Methodenaufrufe war für beide Geräte gleich.

Wenn die App lediglich in den Hintergrund gegangen ist und durch Anklicken einer UILocalNotification die App aufruft, applicationWillEnterForeground:, dann application:didReceiveLocalNotification: und schließlich applicationDidBecomeActive:. Beachten Sie, dass die Reihenfolge der Methodenaufrufe von von der Antwort von @ jaredsinclair abweicht, die vor einigen Jahren geschrieben wurde und wahrscheinlich mit einer anderen Version von iOS getestet wurde.

Wenn die App jedoch beendet wird (durch iOS oder durch den Benutzer, der die App aus dem Multitasking-Side-Scroller herauszieht), tippen Sie auf UILocalNotification, um die App erneut zu starten. Der Aufruf erfolgt nur über applicationDidBecomeActive:. Die Methode application:didReceiveLocalNotification:IS wurde NICHT aufgerufen.

So testete ich die Callback-Sequenz der App-Delegatenmethode: In dem App-Delegaten erstellte ich eine NSMutableArray und füllte sie mit einer Zeichenfolge, wenn applicationWillEnterForeground:, application:didReceiveLocalNotification: und applicationDidBecomeActive: aufgerufen wurden. Dann habe ich den Inhalt des Arrays von den letzten beiden Methoden angezeigt, da ich nicht sicher war, in welcher Reihenfolge sie aufgerufen werden würden. Wenn die App aus dem Hintergrund kommt, bekomme ich nur zwei UIAlertViews, aber nur, weil die beiden genannten Methoden nacheinander aufgerufen werden.

In jedem Fall möchte ich auch die Schlussfolgerung vortragen, dass es nicht möglich ist nachzuverfolgen, ob die App von einem UILocalNotificationgestartet wurde, wenn die App aus einem beendeten Status stammt. Jemand möchte helfen, den Test zu reproduzieren?

0
Matthew Quiros

Ok, hier ist meine letzte und elegante Lösung für Sie, um auf die als private Struktur deklarierten _applicationFlags in UIApplication zugreifen zu können. Erstellen Sie zuerst einen Header "ApplicationFlag.h":

//
//  ApplicationFlag.h
//  PHPConnectDemo
//
//  Created by Paul on 5/18/11.
//  Copyright 2011 [email protected] All rights reserved.
//

#import <Foundation/Foundation.h>
#ifndef APP_FLAG
#define APP_FLAG
#define APP_FLAG_OFFSET 15
#endif

struct appFlag {
    unsigned int isActive:1;
    unsigned int isSuspended:1;
    unsigned int isSuspendedEventsOnly:1;
    unsigned int isLaunchedSuspended:1;
    unsigned int calledNonSuspendedLaunchDelegate:1;
    unsigned int isHandlingURL:1;
    unsigned int isHandlingRemoteNotification:1;
    unsigned int isHandlingLocalNotification:1;
    unsigned int statusBarShowsProgress:1;
    unsigned int statusBarRequestedStyle:4;
    unsigned int statusBarHidden:1;
    unsigned int blockInteractionEvents:4;
    unsigned int receivesMemoryWarnings:1;
    unsigned int showingProgress:1;
    unsigned int receivesPowerMessages:1;
    unsigned int launchEventReceived:1;
    unsigned int isAnimatingSuspensionOrResumption:1;
    unsigned int isResuming:1;
    unsigned int isSuspendedUnderLock:1;
    unsigned int isRunningInTaskSwitcher:1;
    unsigned int shouldExitAfterSendSuspend:1;
    unsigned int shouldExitAfterTaskCompletion:1;
    unsigned int terminating:1;
    unsigned int isHandlingShortCutURL:1;
    unsigned int idleTimerDisabled:1;
    unsigned int deviceOrientation:3;
    unsigned int delegateShouldBeReleasedUponSet:1;
    unsigned int delegateHandleOpenURL:1;
    unsigned int delegateOpenURL:1;
    unsigned int delegateDidReceiveMemoryWarning:1;
    unsigned int delegateWillTerminate:1;
    unsigned int delegateSignificantTimeChange:1;
    unsigned int delegateWillChangeInterfaceOrientation:1;
    unsigned int delegateDidChangeInterfaceOrientation:1;
    unsigned int delegateWillChangeStatusBarFrame:1;
    unsigned int delegateDidChangeStatusBarFrame:1;
    unsigned int delegateDeviceAccelerated:1;
    unsigned int delegateDeviceChangedOrientation:1;
    unsigned int delegateDidBecomeActive:1;
    unsigned int delegateWillResignActive:1;
    unsigned int delegateDidEnterBackground:1;
    unsigned int delegateWillEnterForeground:1;
    unsigned int delegateWillSuspend:1;
    unsigned int delegateDidResume:1;
    unsigned int userDefaultsSyncDisabled:1;
    unsigned int headsetButtonClickCount:4;
    unsigned int isHeadsetButtonDown:1;
    unsigned int isFastForwardActive:1;
    unsigned int isRewindActive:1;
    unsigned int disableViewGroupOpacity:1;
    unsigned int disableViewEdgeAntialiasing:1;
    unsigned int shakeToEdit:1;
    unsigned int isClassic:1;
    unsigned int zoomInClassicMode:1;
    unsigned int ignoreHeadsetClicks:1;
    unsigned int touchRotationDisabled:1;
    unsigned int taskSuspendingUnsupported:1;
    unsigned int isUnitTests:1;
    unsigned int requiresHighResolution:1;
    unsigned int disableViewContentScaling:1;
    unsigned int singleUseLaunchOrientation:3;
    unsigned int defaultInterfaceOrientation:3;
};

@interface ApplicationFlag : NSObject {
    struct appFlag* _flags;
}

@property (nonatomic,assign) struct appFlag* _flags;

@end

Dann erstellen Sie eine Implimentation "ApplicationFlag.m":

//
//  ApplicationFlag.m
//  PHPConnectDemo
//
//  Created by Paul on 5/18/11.
//  Copyright 2011 [email protected] All rights reserved.
//

#import "ApplicationFlag.h"

@implementation ApplicationFlag

@synthesize _flags;

- (id)init
{
    self = [super init];
    if (self) {
        // Custom initialization
        _flags = (id*)[UIApplication sharedApplication]+APP_FLAG_OFFSET;
    }
    return self;
}

@end

Führen Sie dann die übliche Initialisierung in Ihrem Anwendungsdelegierten zusammen mit der Eigenschaft durch, synthetisieren Sie, schließen Sie ein, was auch immer:

applicationFlags = [[ApplicationFlag alloc] init];

Dann können Sie sich auf die Flaggen beziehen:

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    /*
     Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
     */
    if (!applicationFlags._flags->isHandlingURL) {
        //Do whatever you want here
    }
}
0
Paul
- (void)application:(UIApplication *)application didReceiveLocalNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateActive )
        // app was already in the foreground
    else
        // app was just brought from background to foreground

}
0
user2882527