IOS 如何处理多个远程通知点击
How to handle multiple remote-notification click in IOS
我正在尝试根据 notification
单击打开不同的视图控制器,但是当收到 push notification
时,它会自动在后台运行控制器代码,所以当我通过应用程序 [=27= 打开应用程序时] 从 notification centre
它会立即加载 view controller
但是当收到多个通知时它会加载第一个通知控制器,而不管哪个通知被点击。
Let's say I've got notification with titles "Evening","Morning" and
"Night", it should open "Evening View Controller" when "Evening"
notification is tapped but it loads "Night View Controller" when I go
back it loads "Morning View Controller" and at last it loads "Evening
View Controller". Before notification came, I was on Main Controller
and then I moved the app into background.
当我点击 "Morning" 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
为此,需要在 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 并在此方法中推送视图控制器。
*注意 -
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a push
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
谢谢。
我没有尝试过,但我想这个应用程序委托方法可以解决您的问题。
application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:
通过从远程通知中选择一个操作激活您的应用程序时调用此方法。
userInfo
字典参数包含与远程通知相关的信息。
有关详细信息,请参阅苹果文档
这个问题必然会发生,因为您在后台处理通知,所以当您尝试从通知中心启动您的应用时,您的应用已经准备好显示之前收到的通知。
因此,要解决您的问题,您应该
- 检查并决定在
- (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 之后启动相关通知。
首先,您的 Remote Notification
后台模式已启用,因此通知会在后台处理。现在的错误是,无论何时收到通知,您都会将 viewController 推送到 viewController 堆栈,这就是为什么您会看到三个 viewController 堆叠在一起的原因。
其次,didReceiveRemoteNotification
在不同状态下可以called/handled。 StateBackground
如果应用程序在后台,StateInactive
如果用户点击通知中心的通知,StateForeground
如果应用程序在前台。您添加支票仅推送 StateBackground
中的 viewController,这就是为什么您在点击通知时看不到任何变化的原因。
您可以使用不同的应用程序状态。
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.
}
}
我正在尝试根据 notification
单击打开不同的视图控制器,但是当收到 push notification
时,它会自动在后台运行控制器代码,所以当我通过应用程序 [=27= 打开应用程序时] 从 notification centre
它会立即加载 view controller
但是当收到多个通知时它会加载第一个通知控制器,而不管哪个通知被点击。
Let's say I've got notification with titles "Evening","Morning" and "Night", it should open "Evening View Controller" when "Evening" notification is tapped but it loads "Night View Controller" when I go back it loads "Morning View Controller" and at last it loads "Evening View Controller". Before notification came, I was on Main Controller and then I moved the app into background.
当我点击 "Morning" 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
为此,需要在 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 并在此方法中推送视图控制器。
*注意 -
Use this method to process incoming remote notifications for your app. Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background. In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a push notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.
谢谢。
我没有尝试过,但我想这个应用程序委托方法可以解决您的问题。
application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:
通过从远程通知中选择一个操作激活您的应用程序时调用此方法。
userInfo
字典参数包含与远程通知相关的信息。
有关详细信息,请参阅苹果文档
这个问题必然会发生,因为您在后台处理通知,所以当您尝试从通知中心启动您的应用时,您的应用已经准备好显示之前收到的通知。
因此,要解决您的问题,您应该
- 检查并决定在
- (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 之后启动相关通知。
首先,您的 Remote Notification
后台模式已启用,因此通知会在后台处理。现在的错误是,无论何时收到通知,您都会将 viewController 推送到 viewController 堆栈,这就是为什么您会看到三个 viewController 堆叠在一起的原因。
其次,didReceiveRemoteNotification
在不同状态下可以called/handled。 StateBackground
如果应用程序在后台,StateInactive
如果用户点击通知中心的通知,StateForeground
如果应用程序在前台。您添加支票仅推送 StateBackground
中的 viewController,这就是为什么您在点击通知时看不到任何变化的原因。
您可以使用不同的应用程序状态。
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.
}
}