testing应用是否通过UILocalNotification激活

有没有办法知道应用程序是否从本地通知变为活动状态?

我知道有一种方法来testing应用程序是否从本地通知警报启动; 但如果它只是坐在那里的背景下,并收到通知?

当应用程序变为活动状态时,我需要运行不同的代码:

  1. 从本地通知。
  2. 刚刚变得活跃:)

有没有办法做到这一点?

我害怕Sylter是不正确的。 当应用程序从后台进入前台时,无论是通过直接用户操作还是用户对UILocalNotification响应,都不会触发applicationDidFinishLaunchingWithOptions 。 不过,它会调用applicationWillEnterForegroundapplicationDidBecomeActive 。 这可以用一些NSLogs来validation。

所以,问题仍然存在:如果应用程序正在从后台进入前台,则无法发现应用程序是否正在进入前台以响应用户对UILocalNotification的响应,或者仅仅是进入前台。 除…

一旦应用程序进入前台,它将接收方法application:DidReceiveLocalNotification :如果应用程序进入前台以响应UILocalNotification

问题是在application:DidReceiveLocalNotification:进行的任何UI更改application:DidReceiveLocalNotification:方法响应接收到的UILocalNotification发生应用程序已经进入前台后,为用户创造一个令人不安的经验。

有没有人find解决scheme?

我从@ naveed的提示中得到了解决scheme的线索,在调用didReceiveNotification方法时检查应用程序的状态。 当应用程序从后台恢复时,无需检查variables等。

在iOS7及更低版本中,您可以像这样处理通知:

 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { if (application.applicationState == UIApplicationStateInactive ) { //The application received the notification from an inactive state, ie 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. } } 

iOS 8更新:现在通过通知从后台打开应用程序时,将调用以下方法。

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

如果在应用程序处于前台时收到通知,请使用以下方法:

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

请注意,除非您想在应用程序中支持较早版本的操作系统,否则不需要检查应用程序状态。

您可以通过以下方式检查应用程序是否正在运行。

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

就像4:48 AM,现在我突然在床上醒来,find了解决这个问题的办法!

我所做的是,我testing了两个场景,一个是通过点击图标,另一个是通过URL调用,将应用程序放回前台,并比较了UIApplication中的所有variables,并且我终于find了我正在寻找的东西在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; 

这可能包含程序员希望在应用程序返回到前台时访问的所有信息,特别是,如果应用程序由系统pipe理员放在前台,我想访问标记“isHandlingURL”如果应用程序被用户置于前台,则调用0。

接下来,我查看了“application”和“_applicationFlags”的地址,注意到它们被0x3C偏移了60,所以我决定使用地址操作来获得我需要的位:

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

打印输出testing:4a400120x04a40012,如果我写一个完整的长字格式。 这给了我二进制0000 0100 1010 0100 0000 0000 0001 0010 。 回头看看_applicationFlags,这会给我们从LSB的第6位“isHandlingURL”,它是0.现在,如果我试图把应用程序放到后台并用URL调用回来,我得到一个4a40032打印出来在二进制是0000 0100 1010 0100 0000 0000 0011 0010 ,我有我的isHandlingURL位打开! 所以剩下要做的就是通过位移操作完成声明,最终的代码如下所示:

 - (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. */ id* app = (id*)[UIApplication sharedApplication]+15; BOOL isHandlingURL = ((Byte)*app>>5&0x1); if (isHandlingURL) { //do whatever I wanna do here } } 

我可以继续编写一个完整的函数来parsing出所有的_applicationFlag,但是在这个时候,在模拟器和目标上的地址增量是固定的15是不确定的,我的下一个目标就是replace为magic数字“15”由一些macros定义或从系统的价值,所以我可以肯定,它将始终移位0x3C根据需要,我需要查看UIApplication标头,以确保_applicationFlag将总是移位0x3C。

目前为止就这样了!

在你的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, ie The app has become active } return YES; 

}

或者,如果您想知道应用程序在前台还是后台,可以使用以下方法:

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

好吧,这里是我的最后一个优雅的解决scheme,可以访问在UIApplication中声明为私有结构体的_applicationFlags。 首先创build一个头文件“ApplicationFlag.h”:

 // // ApplicationFlag.h // PHPConnectDemo // // Created by Paul on 5/18/11. // Copyright 2011 Paul.Poyu.Lu@gmail.com. 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 

然后创build一个implimentation“ApplicationFlag.m”:

 // // ApplicationFlag.m // PHPConnectDemo // // Created by Paul on 5/18/11. // Copyright 2011 Paul.Poyu.Lu@gmail.com. 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 

然后在您的应用程序委托中进行通常的初始化以及属性,综合,包括…任何:

 applicationFlags = [[ApplicationFlag alloc] init]; 

那么你可以开始引用标志:

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

为了解决这个问题,我刚刚testing了从本地通知启动我的应用程序,并监视调用应用程序委托方法的顺序。 我的testing设备是运行iOS 7.1.1的iPod Touch第五代以及运行iOS 7.1.1的iPhone 4S。 方法调用的顺序对于两个设备是相同的。

如果应用程序只是到了后台,点击一个UILocalNotification来启动应用程序,调用applicationWillEnterForeground: ,然后是application:didReceiveLocalNotification: UILocalNotificationUILocalNotification 请注意,方法调用的顺序与@ jaredsinclair的答案不同 ,后者是几年前编写的,可能在不同版本的iOS上进行了testing。

如果应用程序被终止(通过iOS或用户从多任务侧滚动器中滑出应用程序),则点击UILocalNotification以再次启动应用程序,只调用applicationDidBecomeActive: UILocalNotification 方法application:didReceiveLocalNotification: 不被调用。

我如何testing应用程序委托方法callback序列:在应用程序委托中,我创build了一个NSMutableArray并在applicationWillEnterForeground:application:didReceiveLocalNotification:applicationDidBecomeActive:被调用时用string填充它。 然后,我显示了最后两个方法中的数组内容,因为我不确定它们按什么顺序被调用。 当应用程序来自后台,只有当我得到两个UIAlertView ,但只是因为两个所述的方法被一个接一个地调用。

无论如何,我还想推出一个UILocalNotification 如果应用程序来自终止状态 ,那么无法跟踪是否从UILocalNotification启动应用程序。 有人想通过复制testing来确认吗?

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