五、<iOS 远程推送> iOS 10 UNNoti
研究完苹果 iOS 10 推送后,到9月份,等苹果 iOS 11 正式上线,再研究其推送,不愿意踩坑,敬请期待。
如果我们想自定义通知的界面,提供用户更多丰富的界面展示?我们该怎么做?UNNotificationContentExtension 为我们做好了一切。让我们看一下的效果图,然后我去读取 UNNotificationContentExtension API 和动手做 Demo。
图 1 图 2上图中的推送,可以分为四部分,第一部分是自定义标题,第二部分是自定义推送界面,第三部分是推送的默认内容,第四部分是推送 Action。
一、UNNotificationContentExtension API 研读
UNNotificationContentExtension 让本地推送和远程推送都能自定义界面。
图 3 图 4在图 4 中,红色标记可以看到 NSExtension 字典中有三个 key,其中 NSExtensionAttribute 也是一个字典,它也需要我们手动去设置 key, NSExtensionAttribute 的 key 说明如下:
-
UNNotificationExtensionCategory. (一定需要配置) ,此 value 是字符串,UNNotificationCategory 类中的标识,一定要和开始注册通知的UNNotificationCategory 标识相同。
-
UNNotificationExtensionInitialContentSizeRatio. (一定需要配置) 此 value 是浮点型。当extension 加载的时候,系统会利用此值来设置 ViewController 的 size。如果设置该值是 0.5,那个 ViewController 的长度是宽度的一半;
-
UNNotificationExtensionDefaultContentHidden. (可以不配置,默认是 NO), value 是 Bool 值。如果设置 NO 时 图5(下图) 的默认内容就不会展示。如果是 YES 时, 图2 的默认内容将会展示。
-
UNNotificationExtensionOverridesDefaultTitle (可以不配置,默认是 NO), value 是 Bool 值,如果设置为 YES 时, ViewController 的 title
图 5
属性,可以推送的标题。建议是在 didReceiveNotification 方法中设置。如果设置为 NO 时,默认是可以推送的标题是用应用的标题。图 5(下图) 中 自定义标题 是推送的标题
当用户点击 Action 时,会调用 didReceiveNotificationResponse:completionHandler:
方法。
注意,本章节不会讲述 在自定推送界面加入 Media 讲解。请参考 iOS10推送必看UNNotificationContentExtension。
二、如何创建 推送 Extension
-
1、在 Xcode -> File -> Target
图 6 -
2、选取 Notification
- 3、给 Extension 文件命名,并点击完成
三、代码的实现
关于如何注册通知和配置证书,请参考 三、<iOS 远程推送> 静默推送 。
- 1、项目目录结构
- 2、全部代码
A、在 AppDelegate.m 中
#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//ios 10 版本以上的通知
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
//注册通知
[center requestAuthorizationWithOptions:UNAuthorizationOptionBadge| UNAuthorizationOptionSound|UNAuthorizationOptionAlert|UNAuthorizationOptionCarPlay completionHandler:^(BOOL granted, NSError * _Nullable error) {
NSLog(@"错误的要求通知,%@",error);
}];
// Action 内容 UNNotificationActionOptionForeground 点击 actionbutton 会跳转到应用中
UNNotificationAction *actionOne = [UNNotificationAction actionWithIdentifier:@"action1" title:@"Ross" options:UNNotificationActionOptionForeground];
UNNotificationAction *actionTwo = [UNNotificationAction actionWithIdentifier:@"action2" title:@"Chandler" options:UNNotificationActionOptionForeground];
UNNotificationAction *actionThree = [UNNotificationAction actionWithIdentifier:@"action3" title:@"Joey" options:UNNotificationActionOptionForeground];
UNTextInputNotificationAction *actionFour = [UNTextInputNotificationAction actionWithIdentifier:@"action4" title:@"inputContent" options:UNNotificationActionOptionForeground textInputButtonTitle:@"测试"textInputPlaceholder:@"请输入内容"];
//分类
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"myNotificationCategory1" actions:@[actionOne,actionTwo,actionThree, actionFour] intentIdentifiers:@[] options:0];
//添加分类
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
// Override point for customization after application launch.
return YES;
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(@"%@",[NSString stringWithFormat:@"%@",deviceToken]);
}
- (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 invalidate graphics rendering callbacks. 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, this method is called instead of applicationWillTerminate: when the user quits.
}
- (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.
}
- (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. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
B、在 NotificationViewController.m 中
#import "NotificationViewController.h"
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>
@interface NotificationViewController () <UNNotificationContentExtension>
@property IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIImageView *selectedImage;
@end
@implementation NotificationViewController
- (void)viewDidLoad {
[super viewDidLoad]; // CGSize(width: selsize.width, height: size.height / 4.0)
self.preferredContentSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height/3);
[self loadImageWithName:@"chandler"];
// Do any required interface initialization here.
NSLog(@"1");
}
- (void)didReceiveNotification:(UNNotification *)notification {
NSLog(@"2");
self.title = @"helloWorld";
NSDictionary *dict = notification.request.content.userInfo;
NSDictionary *noticeDict = dict[@"aps"];
//NSLog(@"%@",noticeDict);
//设置通知标题和内容
// self.label.text = noticeDict[@"hi"];
UNMutableNotificationContent *mutableContent = [notification.request.content mutableCopy];
mutableContent.title = noticeDict[@"tittle"];
mutableContent.subtitle = noticeDict[@"subtittle"];
}
// If implemented, the method will be called when the user taps on one
// of the notification actions. The completion handler can be called
// after handling the action to dismiss the notification and forward the
// action to the app if necessary.
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion{
if ([response.actionIdentifier isEqualToString:@"action1"]) {
[self loadImageWithName:@"ross"];
self.label.text = @"That's the correct answer!";
}else if([response.actionIdentifier isEqualToString:@"action2"]){
[self loadImageWithName:@"chandler"];
self.label.text = @"Could you BE more wrong!";
}else if([response.actionIdentifier isEqualToString:@"action3"]){
[self loadImageWithName:@"joey"];
self.label.text = @"Try again... or go eat a sandwich.";
}else{
self.label.text = [(UNTextInputNotificationResponse *)response userText];
}
completion(UNNotificationContentExtensionResponseOptionDoNotDismiss);
}
-(void)loadImageWithName:(NSString*)imageName{
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"] ;
NSLog(@"==%@",imagePath);
self.selectedImage.image = [UIImage imageWithContentsOfFile:imagePath];
}
@end
C、Storyboard 布局和 info.plist 的配置
图 9 图 10四、推送的内容
提供一个强大的基于 MAC 的推送服务器,SmartPush。运行 Xcode 会出现如下图的弹框。
//iOS10 之前
{
"aps" : {
"alert" : "title",
"badge" : 1,
"sound":"default"
},
}
//iOS 10 推送的内容
{
"aps":{
"alert":{
"title" : "标题",
"subtitle" : "副标题",
"body" : "Copyright © 2017年 OliverWang All rights reserved."
},
"badge":1,
"sound":"default",
"category":"myNotificationCategory1", "tittle":"You lover !", "hi":"Hello World",
}
}
五、推送的注意点
- A、代码不能修改 图2 中默认通知标题,副标题以及内容,只能通过服务器再推送。在上面 didReceiveNotification 方法中,有一如下的代码,其实是没有作用的,提供读者验证。
self.title = @"helloWorld";
NSDictionary *dict = notification.request.content.userInfo;
NSDictionary *noticeDict = dict[@"aps"];
//NSLog(@"%@",noticeDict);
//设置通知标题和内容
// self.label.text = noticeDict[@"hi"];
UNMutableNotificationContent *mutableContent = [notification.request.content mutableCopy];
mutableContent.title = noticeDict[@"tittle"];
mutableContent.subtitle = noticeDict[@"subtittle"];
如果上面的内容有不对之处,请帮忙指出,感谢!