سؤال

هل هناك طريقة لمعرفة ما إذا كان التطبيق نشطًا من إشعار محلي؟

أعلم أن هناك طريقة لاختبار ما إذا كان التطبيق أطلقت من تنبيه إشعار محلي ؛ ولكن إذا كان مجرد الجلوس هناك الخلفية ، وتلقى إشعارًا؟

أحتاج إلى تشغيل رمز مختلف ، عندما يصبح التطبيق نشطًا:

  1. من إشعار محلي.
  2. فقط أصبح نشطا :)

هل هناك طريقة للقيام بذلك؟

هل كانت مفيدة؟

المحلول

أخشى أن يكون سيلتر غير صحيح. عندما يدخل التطبيق المقدمة من الخلفية, ، إما عن طريق إجراء مستخدم مباشر أو استجابة المستخدم إلى UILocalNotification, ، لا يؤدي applicationDidFinishLaunchingWithOptions. ومع ذلك ، فإنه يدعو applicationWillEnterForeground و applicationDidBecomeActive. يمكن التحقق من ذلك مع اثنين من NSLogs.

لذا ، تبقى المشكلة: إذا كان التطبيق يدخل المقدمة من الخلفية ، فلا توجد طريقة لاكتشاف ما إذا كان التطبيق يدخل المقدمة استجابةً لاستجابة المستخدم إلى أ UILocalNotification, ، أو إذا كان مجرد دخول المقدمة. إلا...

بمجرد أن يدخل التطبيق المقدمة ، سيتلقى الطريقة application:DidReceiveLocalNotification: إذا دخل التطبيق المقدمة استجابة لـ UILocalNotification.

المشكلة هي أن أي تغييرات واجهة المستخدم التي تم إجراؤها داخل application:DidReceiveLocalNotification: الطريقة استجابة لاستلام UilocalNotification تحدث بعد، بعدما لقد دخل التطبيق بالفعل في المقدمة ، مما يخلق تجربة مزعجة للمستخدم.

هل وجد أحد الحل؟

نصائح أخرى

حصلت على أدنى فكرة عن الحل لهذا من طرف @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 {
}

لاحظ أنه ليست هناك حاجة للتحقق من حالة التطبيق إلا إذا كنت ترغب في دعم الإصدارات القديمة من نظام التشغيل في التطبيق الخاص بك.

يمكنك التحقق من سيناريوهات أي من التطبيقات قيد التشغيل أم لا عند استلام الطلب بالمتابعة.

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

ما فعلته هو ، لقد اختبرت سيناريوهين ، أحدهما هو إعادة التطبيق إلى المقدمة من خلال النقر على الأيقونة ، وآخر بواسطة مكالمة عنوان URL ، وقارن جميع المتغيرات داخل 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 إذا تم وضع التطبيق في المقدمة من قبل المستخدم.

بعد ذلك ، نظرت إلى عنوان "التطبيق" و "_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);
}

الذي يطبع الاختبار: 4A40012, ، أو 0x04A40012 إذا كتبت بتنسيق كلمة طويلة كاملة. هذا يعطيني في ثنائي 0000 0100 1010 0100 0000 0000 0001 0010. إذا نظرنا إلى الوراء إلى _applicationFlags ، فإن هذا سيعطينا "ishandlingurl" في الجزء السادس من LSB ، وهو 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 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.

هذا كل شئ حتى الان!

في 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:.
     */
}

حسنًا ، إليك الحل النهائي والأنيق لكي تكون قادرًا على الوصول إلى _ApplicationFlags المعلنة كهيكل خاص داخل UIApplication. أولا قم بإنشاء رأس "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

ثم قم بإنشاء إعفاء "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
    }
}

لإلقاء المزيد من الضوء في المشكلة ، لقد اختبرت للتو إطلاق تطبيقي من إشعار محلي ورصد الترتيب الذي تم فيه استدعاء طرق مندوب التطبيق. كانت أجهزة الاختبار الخاصة بي هي الجيل الخامس من 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: تم استدعاؤهم. بعد ذلك ، عرضت محتويات الصفيف من الطريقتين الأخيرتين لأنني لم أكن متأكدة من الترتيب الذي سيتم استدعاؤه. عندما يأتي التطبيق من الخلفية ، هذا فقط عندما أحصل على اثنين UIAlertViewS ، ولكن فقط لأن الطريقتين المذكورتين تسمى واحدة تلو الأخرى.

على أي حال ، أود أيضًا أن أتقدم إلى الأمام استنتاج مفاده أنه لا توجد طريقة لتتبع ما إذا كان التطبيق قد تم إطلاقه من أ UILocalNotification إذا كان التطبيق قادمًا من حالة إنهاء. شخص ما يريد المساعدة في التأكيد من خلال إعادة إنتاج الاختبار؟

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

}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top