web-dev-qa-db-ja.com

アプリがUILocalNotificationからアクティブになったかどうかをテストする

アプリケーションがローカル通知からアクティブになったかどうかを知る方法はありますか?

アプリケーションがローカル通知アラートから起動であったかどうかをテストする方法があることを知っています。しかし、それが背景に座っていて、通知を受け取っただけの場合はどうでしょうか。

アプリがアクティブになったときに、別のコードを実行する必要があります。

  1. ローカル通知から。
  2. ちょうどアクティブになっています:)

それを行う方法はありますか?

43
wh1t3cat1k

シルターが間違っていると思います。アプリがフォアグラウンドバックグラウンドからに入るとき、直接のユーザーアクションまたはUILocalNotificationへのユーザー応答によって、applicationDidFinishLaunchingWithOptionsはトリガーされません。ただし、applicationWillEnterForegroundおよびapplicationDidBecomeActiveを呼び出します。これはいくつかのNSLogsで確認できます。

したがって、問題は残ります。アプリがバックグラウンドからフォアグラウンドに入っている場合、UILocalNotificationに対するユーザーの応答に応じてアプリがフォアグラウンドに入っているか、それとも単にフォアグラウンドに入ります。を除いて...

アプリがフォアグラウンドに入ると、UILocalNotificationへの応答としてアプリがフォアグラウンドに入った場合、application:DidReceiveLocalNotification:メソッドを受け取ります。

問題は、UILocalNotificationの受信に応答してapplication:DidReceiveLocalNotification:メソッド内で行われたUIの変更が発生することですafterアプリがすでにフォアグラウンドに入っており、ユーザーに不快な体験を生み出しています。

誰かが解決策を見つけましたか?

29
jaredsinclair

@naveedの、didReceiveNotificationメソッドが呼び出されたときのアプリケーションの状態を確認する際のヒントから、この解決策の手がかりが得られました。アプリがバックグラウンドから再開するときに変数などを確認する必要はありません。

iOS7以下通知は次のように処理します。

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

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

アプリで古いバージョンのOSをサポートする場合を除き、アプリケーションの状態を確認する必要はありません。

63
Michael Gaylord

以下の手順でアプリケーションを受信したときに、いずれかのアプリケーションのシナリオが実行されているかどうかを確認できます。

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

私がしたことは、2つのシナリオをテストしました.1つは、アイコンをクリックしてアプリをフォアグラウンドに戻すこと、もう1つはURL sys呼び出しを使用して、UIApplication内のすべての変数を比較することでした。 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;

これには、アプリケーションがフォアグラウンドに戻ったときにプログラマがアクセスして欲しいすべての情報が含まれている可能性があります。具体的には、「isHandlingURL」フラグにアクセスします。これは、アプリがシステムによってフォアグラウンドに置かれた場合に1と表示されます。呼び出し、アプリがユーザーによってフォアグラウンドになった場合は0。

次に、 "application"と "_applicationFlags"のアドレスを見て、それらが0x3C(60)でオフセットされていることに気付いたので、アドレス操作を使用して必要なビットを取得することにしました。

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

test:4a40012、またはx04a40012を出力する場合、完全な長いWord形式で書き込みます。これにより、バイナリで00 0100 1010 0100 0000 0000 0001 001が得られます。 _applicationFlagsを振り返ると、LSBの6番目のビットである「isHandlingURL」が0になります。アプリをバックグラウンドにして、URLのsys呼び出しで戻そうとすると、印刷結果が表示されます- 4a40032バイナリでは00 0100 1010 0100 0000 0000 0011 001そして、isHandlingURLビットをオンにしています!あとは、ビットシフト操作でステートメントを完成させるだけで、最終的なコードは次のようになります。

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

続けて、すべての_applicationFlagを解析する完全な関数を書くことができますが、この時点で、シミュレーターとターゲットの両方でアドレスの増分が15に固定されているかどうかは不明です。次の目標は、マジックに置き換えることです。いくつかのマクロ定義またはシステムからの値による数値「15」なので、必要に応じて常に0x3Cをシフトし、_applicationFlagが常に0x3Cだけシフトすることを確認するためにUIApplicationヘッダーを調べる必要があります。

それは今のところすべてです!

7
Paul

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

または、アプリケーションがフォアグラウンドまたはバックグラウンドのどちらにあるかを知りたい場合は、このメソッドを使用できます。

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

問題をさらに詳しく理解するために、ローカル通知からのアプリの起動をテストし、アプリデリゲートメソッドが呼び出される順序を監視しました。私のテストデバイスは、iOS 7.1.1を実行している第5世代iPod Touch、およびiOS 7.1.1を実行しているiPhone 4Sでした。メソッド呼び出しの順序は、両方のデバイスで同じでした。

アプリがバックグラウンドに移行しただけの場合、UILocalNotificationをタップしてアプリを起動すると、applicationWillEnterForeground:、次にapplication:didReceiveLocalNotification:、最後にapplicationDidBecomeActive:が呼び出されます。メソッド呼び出しのシーケンスは、@ jaredsinclairの回答とはdifferentであることに注意してください。これは数年前に書かれ、おそらくiOSの別のバージョンでテストされました。

ただし、アプリが終了した場合(iOSまたはユーザーがマルチタスクサイドスクローラーからアプリをスワイプして)、UILocalNotificationをタップしてアプリを再度起動すると、applicationDidBecomeActive:のみが呼び出されます。メソッドapplication:didReceiveLocalNotification:は呼び出されません。

アプリデリゲートメソッドのコールバックシーケンスをテストする方法:アプリデリゲートで、NSMutableArrayを作成し、applicationWillEnterForeground:application:didReceiveLocalNotification:、およびapplicationDidBecomeActive:が呼び出されるたびに文字列を入力しました。次に、最後の2つのメソッドの配列の内容を表示しました。それらが呼び出される順序がわからなかったためです。アプリがバックグラウンドで実行される場合、それは私が2つのUIAlertViewsを取得したときだけですが、2つのメソッドが順番に呼び出されるためです。

いずれの場合でも、アプリが終了状態からのものである場合、アプリがUILocalNotificationから起動されたかどうかを追跡する方法がないという結論を押し進めたいと思います。テストを再現して確認を手伝いたい人はいますか?

0
Matthew Quiros

ここで、UIApplication内のプライベート構造体として宣言されている_applicationFlagsにアクセスできるようにするための、私の最後のエレガントなソリューションを紹介します。最初にヘッダー「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

次に、実装「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

次に、通常の初期化をアプリケーションデリゲートで、プロパティ、synthese、includesと一緒に行います。

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