Подтвердить что ты не робот

Проверьте, активировано ли приложение из UILocalNotification

Есть ли способ узнать, стало ли приложение активировано из локального уведомления?

Я знаю, что есть способ проверить, было ли приложение запущено из локального уведомления; но если он просто сидел там фон и получил уведомление?

Мне нужно запустить другой код, когда приложение стало активным:

  • От локального уведомления.
  • Только что стал активным:)

Есть ли способ сделать это?

4b9b3361

Ответ 1

Я боюсь, что Сильтер неверен. Когда приложение входит на передний план из фона, либо прямым действием пользователя, либо ответом пользователя на UILocalNotification, он не запускает applicationDidFinishLaunchingWithOptions. Однако он вызывает applicationWillEnterForeground и applicationDidBecomeActive. Это можно проверить с помощью пары NSLogs.

Итак, проблема остается: если приложение вводит передний план из фона, нет способа узнать, входит ли приложение в передний план в ответ на ответ пользователя на UILocalNotification, или если это просто выйдя на передний план. За исключением...

Как только приложение вступит на передний план, оно получит метод application:DidReceiveLocalNotification: если приложение ввело передний план в ответ на UILocalNotification.

Проблема в том, что любые изменения пользовательского интерфейса, сделанные в методе application:DidReceiveLocalNotification: в ответ на получение UILocalNotification, происходят после того, как приложение уже ввело передний план, создавая тревожные ощущения для пользователя.

Кто-нибудь нашел решение?

Ответ 2

Я получил ключ к решению этого вопроса от @naveed tip при проверке состояния приложения при вызове метода 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 {
}

Обратите внимание, что нет необходимости проверять состояние приложения, если вы не хотите поддерживать более старые версии ОС в своем приложении.

Ответ 3

Вы можете проверить сценарии работы любого приложения или нет, когда приложение получено следующим образом.

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

Что я сделал, я протестировал два сценария, один - вернуть приложение на передний план, щелкнув значок, другой по 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, если приложение помещено на передний план sys-call, 0, если приложение будет помещено на передний план пользователем.

Затем я просмотрел адрес "application" и "_plplicationFlags", заметил, что они смещены на 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);
}

который распечатывает тест: 4a40012, или 0x04a40012, если я напишу в полном формате длинного слова. Это дает мне двоичный 0000 0100 1010 0100 0000 0000 0001 0010. Оглядываясь назад на _applicationFlags, это даст нам "isHandlingURL" на 6-м бите из LSB, который равен 0. Теперь, если я попытаюсь поместить приложение в фоновый режим и вернуть его с помощью вызова sys для 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 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 по мере необходимости, и мне нужно посмотреть в заголовок UIApplication, чтобы убедиться, что _applicationFlag всегда будет сдвигаться на 0x3C.

Это все на данный момент!

Ответ 5

В приложении 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:.
     */
}

Ответ 6

Хорошо, вот мое окончательное и элегантное решение для вас, чтобы иметь доступ к объявлению _applicationFlags, объявленному как частная структура в UIApplication. Сначала создайте заголовок "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

Затем выполните обычную инициализацию в делетете приложения вместе с свойством, синтезируйте, включите... что угодно:

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

Ответ 7

Чтобы пролить больше света на проблему, я только что тестировал запуск моего приложения из локального уведомления и контролировал порядок, по которому были вызваны методы делегирования приложения. Мои тестовые устройства были пятым поколением iPod Touch под управлением iOS 7.1.1 и iPhone 4S под управлением iOS 7.1.1. Порядок вызовов методов был одинаковым для обоих устройств.

Если приложение просто ушло на задний план, нажав на UILocalNotification, чтобы запустить приложение, вызовите applicationWillEnterForeground:, затем application:didReceiveLocalNotification: и, наконец, applicationDidBecomeActive:. Обратите внимание, что последовательность вызовов методов отличается от ответа @jaredsinclair, который был написан несколько лет назад и, вероятно, протестирован в другой версии iOS.

Если приложение, однако, завершено (iOS или пользователем, выбирая приложение из многозадачного бокового скроллера), нажатие на UILocalNotification для запуска приложения снова вызывает только applicationDidBecomeActive:. Метод application:didReceiveLocalNotification: НЕ НАЗНАЧЕН.

Как я протестировал последовательность обратного вызова метода делегата приложения: в делегате приложения я создал NSMutableArray и заполнил его строкой при вызове applicationWillEnterForeground:, application:didReceiveLocalNotification: и applicationDidBecomeActive:. Затем я отобразил содержимое массива из двух последних методов, так как я не был уверен, в каком порядке они будут вызываться. Когда приложение приходит из фона, это происходит только тогда, когда я получаю два UIAlertView s, но только потому, что два указанных метода называются один за другим.

В любом случае, я также хотел бы сделать вывод о том, что нет способа отслеживать, было ли приложение запущено с UILocalNotification , если приложение выходит из состояния с завершением. Кто-то хочет помочь подтвердить, воспроизведя тест?

Ответ 8

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

}