如何在IOS中处理多个远程通知点击
Posted
技术标签:
【中文标题】如何在IOS中处理多个远程通知点击【英文标题】:How to handle multiple remote-notification click in IOS 【发布时间】:2016-02-22 08:50:41 【问题描述】:我正在尝试根据notification
click 打开不同的视图控制器,但是当收到push notification
时,它会自动在后台运行控制器代码,因此当我通过来自notification centre
的应用程序图标/通知打开应用程序时,它会加载view controller
立即,但当收到多个通知时,它会加载第一个通知控制器,而不管点击了哪个通知。
假设我收到了标题为“晚上”、“早上”和 “Night”,应该在“Evening”时打开“Evening View Controller” 通知被点击,但当我去时它会加载“夜景控制器” 返回它加载“Morning View Controller”,最后它加载“Evening 查看控制器”。在通知到来之前,我在主控制器上 然后我将应用移到后台。
当我点击“早上”notification
时,它现在什么都不做。
之前我尝试使用 addObserver 但结果是一样的,所以我转向应用程序委托。
这是应用委托代码
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
[[UIApplication sharedApplication]setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
// Override point for customization after application launch.
// Register for Push Notitications, if running ios 8 or More
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
else
// Register for Push Notifications before iOS 8
[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
[application setStatusBarHidden:YES];
return YES;
// Handle remote notification registration.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
NSLog(@"Device Token %@",[self stringWithDeviceToken:deviceToken]);
[[NSUserDefaults standardUserDefaults]setObject:[self stringWithDeviceToken:deviceToken] forKey:@"registration_id"];
[[NSUserDefaults standardUserDefaults] synchronize];
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
NSLog(@"Error in registration. Error: %@", err);
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
NSDictionary *alertInfo = (NSDictionary *) userInfo[@"aps"][@"alert"];
NSLog(@"%@",alertInfo);
NSString *alertID = [userInfo valueForKey:@"alertid"];
if (application.applicationState == UIApplicationStateBackground)
[UIApplication sharedApplication].applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber + 1;
NSLog(@"Background Mode");
NSString *title = alertInfo[@"title"];
[self openViewController:title];
if(application.applicationState == UIApplicationStateActive)
NSLog(@"Active Mode");
completionHandler(UIBackgroundFetchResultNewData);
[self performSelector:@selector(sendAckRequest:) withObject:alertID];
- (void)openViewController:(NSString *)notificationTitle
NSDictionary * userDict = [[NSUserDefaults standardUserDefaults] objectForKey:@"loginUser"];
if([notificationTitle isEqualToString:@"Exercise"])
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
Excercise *excerciseController = (Excercise*)[mainStoryboard instantiateViewControllerWithIdentifier: @"Excercise"];
[navigationController pushViewController:excerciseController animated:YES];
//[navigationController presentViewController:excerciseController animated:YES completion:nil];
else if([notificationTitle isEqualToString:@"Weight"])
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
Weight *weightController = (Weight*)[mainStoryboard instantiateViewControllerWithIdentifier: @"Weight"];
[navigationController pushViewController:weightController animated:YES];
//[navigationController presentViewController:weightController animated:YES completion:nil];
else if([notificationTitle isEqualToString:@"MCQ"])
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
QuestionOfTheDay *questionController = (QuestionOfTheDay*)[mainStoryboard instantiateViewControllerWithIdentifier: @"QuestionOfTheDay"];
questionController.self.dictUser = userDict;
[navigationController pushViewController:questionController animated:YES]
-(void)sendAckRequest:(NSString *)alertID
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *userID =[[defaults objectForKey:@"loginUser"]objectForKey:@"UserId"];
NSString *serverRegistrationID = [defaults objectForKey:@"server_registration_id"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"];
NSString *currentDateString = [dateFormatter stringFromDate:[NSDate date]];
//NSDate *currentDate = [dateFormatter dateFromString:currentDateString];
//NSDictionary *parameter = @@"ReminderDateTime":currentDateString;
NSString *url = [NSString stringWithFormat:@"%@%@/%@/%@/?ReminderDateTime=%@",sendNotificationAck,userID,serverRegistrationID,alertID,currentDateString];
NSLog(@"url: %@",url);
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject)
if(operation.response.statusCode == 200)
NSLog(@"Notification Acknowledged");
else
NSLog(@"Notification failed to acknowledge");
failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error)
NSLog(@"error: %@",[error localizedDescription]);
];
- (NSString *)stringWithDeviceToken:(NSData *)deviceToken
const char *data = [deviceToken bytes];
NSMutableString *token = [NSMutableString string];
for (int i = 0; i < [deviceToken length]; i++)
[token appendFormat:@"%02.2hhX", data[i]];
return token;
- (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.
if([UIApplication sharedApplication].applicationIconBadgeNumber!=0)
[UIApplication sharedApplication].applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber - 1;
@end
【问题讨论】:
你可以使用布尔值来处理它。 【参考方案1】:首先,您的Remote Notification
后台模式已启用,因此通知将在后台处理。现在的错误是,每当收到通知时,您都会将 viewController 推送到 viewController 堆栈,这就是为什么您会看到三个 viewController 堆叠在一起的原因。
其次,didReceiveRemoteNotification
可以在不同的状态下被调用/处理。 StateBackground
如果应用程序在后台,StateInactive
如果用户点击通知中心的通知,StateForeground
如果应用程序在前台。您在StateBackground
中添加了仅推送视图控制器的检查,这就是为什么您在点击通知时看不到任何更改的原因。
【讨论】:
这是否意味着如果我在StateInactive
中添加导航代码,它会起作用吗? @dichen
添加 StateInactive 检查将在您点击通知时处理这种情况。但要小心处理多次,一次用于后台,一次用于非活动。您可能需要一个标志来检查目标 viewController 是否已显示。
我会检查一下,但早些时候我使用的是StateBackground
,当点击通知时它没有运行。我会尝试StateInactive
并让你知道。我应该使用直接视图控制器代码还是添加观察者并尝试?
对不起,我没有遵循这个,“我应该使用直接视图控制器代码还是添加观察者并尝试?”
我应该在StateInActive
或addObserver
内的应用程序委托中使用导航控制器而不是导航控制器【参考方案2】:
为此,需要检查didReceiveRemoteNotification
方法中的应用程序状态。这是代码:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
//recieved notification
if ( application.applicationState == UIApplicationStateActive )
// app was already in the foreground
UIViewController* topViewController = [self.navigationController topViewController];
if ([topViewController isKindOfClass:[HomeVC class]])
[[NSNotificationCenter defaultCenter] postNotificationName:@"getUpdatedServiceData" object:nil];
在 HomeVC 中您需要创建通知 getUpdatedServiceData 并在此方法中推送视图控制器。
*注 -
使用此方法为您的应用处理传入的远程通知。 与 application:didReceiveRemoteNotification: 方法不同,它是 仅当您的应用程序在前台运行时调用,系统 当您的应用在前台运行时调用此方法或 背景。此外,如果您启用了远程通知 后台模式,系统启动您的应用程序(或从 暂停状态)并在推送时将其置于后台状态 通知到达。但是系统不会自动 如果用户强制退出它,则启动您的应用程序。在这种情况下, 用户必须在系统之前重新启动您的应用程序或重新启动设备 尝试再次自动启动您的应用。
谢谢。
【讨论】:
我们在后台收到推送时遇到问题。 在前台,我们显示的是UIAlertView
,所以这对我们来说不是问题【参考方案3】:
我没有尝试过,但我想这个应用程序委托方法会解决你的问题。
application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:
当您的应用已通过从远程通知中选择一个动作被激活时调用此方法。
userInfo
字典参数包含与远程通知相关的信息。
更多信息参见苹果文档
https://developer.apple.com/library/ios//documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:
【讨论】:
此方法从 8.0 开始可用,我们的最低版本是 7.0【参考方案4】:当您在后台处理通知时肯定会发生此问题,因此当您尝试从通知中心启动应用时,您的应用已经准备好显示以前收到的通知。
所以要解决你的问题,你应该
-
检查并决定最新的或点击的通知,或者在
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
或 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中显示的有意义的通知,具体取决于应用状态,这些委托会被调用以获取通知
在应用启动时检查已显示的 viewController。如果是,请删除它们并显示最新或点击的通知。
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIViewController* topViewController = [navigationController visibleViewController];
if ([topViewController isKindOfClass:[Excercise class]])
[topViewController dismissViewControllerAnimated:NO completion:nil];
// 之后启动相关的通知VC。
【讨论】:
【参考方案5】:您可以使用不同的应用状态。
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void)
if ( application.applicationState == UIApplicationState.Active)
// App is foreground and notification is recieved,
// Show a alert.
else if( application.applicationState == UIApplicationState.Background)
// App is in background and notification is received,
// You can fetch required data here don't do anything with UI.
else if( application.applicationState == UIApplicationState.Inactive)
// App came in foreground by used clicking on notification,
// Use userinfo for redirecting to specific view controller.
【讨论】:
以上是关于如何在IOS中处理多个远程通知点击的主要内容,如果未能解决你的问题,请参考以下文章