我们都收到过这种通知,它比普通通知内容更加丰富,可以附带图片、音频、视频等附件,下方也有各种选择项供用户选择。这其中附件功能由NotificationServiceExtension
来提供和实现,选择项功能由UNNotificationAction
和UNNotificationCategory
来提供和实现。
ps: 这些功能均自 iOS10 及以后才提供
一、NotificationServiceExtension
在targets中创建Notification Service Extension,然后生成的文件中有两个方法
didReceive(_:withContentHandler:)
我们用来将图片或音视频包装成attachment,然后再放入通知中。
- 图片或音视频通过通知的附加字段来传输,此时
mutable-content
必须为1 - 此方法的处理时间至多为30秒,如果30秒还未处理完毕,将调用
serviceExtensionTimeWillExpire()
方法 - 因为
UNNotificationAttachment
的初始化方法中的URL只接收FileURL,所以需要将附件先写入本地,再拿到本地URL
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
// 处理标题
// bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
// bestAttemptContent.subtitle = "this is subtitle"
// 处理附件 eg. 图片、视频、动图、音频
// 附件最大尺寸 音频5M,图片10M,视频50M
if let dict = bestAttemptContent.userInfo as? Dictionary, let urlString = dict["attachment"] as? String, let url = URL(string: urlString) {
var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
path!.append(contentsOf: "/" + urlString.split(separator: "/").last!.split(separator: "?").first!)
if ((try? Data(contentsOf: url).write(to: URL(fileURLWithPath: path!))) != nil), let attachment = try? UNNotificationAttachment(identifier: "attachment", url: URL(fileURLWithPath: path!), options: nil) {
bestAttemptContent.attachments = [attachment]
}
}
contentHandler(bestAttemptContent)
}
}
serviceExtensionTimeWillExpire()
此方法当附件处理时间过长时会调用,之后会将通知直接展示
调用时机
关于UNNotificationServiceExtension,系统文档有以下说明
Note
You cannot modify silent notifications or those that only play a sound or badge the app’s icon.
另外mutable-content
必须为1,表示可以修改这条通知
二、自定义notification action
主要有UNNotificationCategory
和UNNotificationAction
两个类起作用。
其中UNNotificationAction
对应用户的某个选项,如 查看、跟贴 等,而UNNotificationCategory
下可以添加多个action,一个通知对应一个category。
发送通知的时候需要将category参数填上想展示的对应categoryID。
UNTextInputNotificationAction
为UNNotificationAction
的子类,点击后可以回复本条通知,就像微信的消息回复。
// MARK: - 通知action的处理
// 配置action
fileprivate func setupNotificationAction() {
guard #available(iOS 10.0, *) else { return }
// options参数:
// .foreground 白底黑字,触发后app打开(进入前台)
// .authenticationRequired 白底黑字,触发后不进入app
// .destructive 白底红字,触发后不进入app
let action1 = UNNotificationAction(identifier: notificationActionIdentifierScan, title: "查看", options: .foreground)
let action2 = UNNotificationAction(identifier: notificationActionIdentifierDismiss, title: "不感兴趣", options: .authenticationRequired)
let action3 = UNTextInputNotificationAction(identifier: notificationActionIdentifierReply, title: "回复", options: .authenticationRequired, textInputButtonTitle: "发送", textInputPlaceholder: "请输入内容")
let category1 = UNNotificationCategory(identifier: notificationCategoryNormal, actions: [action1, action2], intentIdentifiers: [notificationActionIdentifierScan, notificationActionIdentifierDismiss], options: .customDismissAction)
let category2 = UNNotificationCategory(identifier: notificationCategorySession, actions: [action3, action1], intentIdentifiers: [notificationActionIdentifierScan, notificationActionIdentifierReply], options: .customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories([category1, category2])
}
点击某个选项后进入app后依旧是调用userNotificationCenter(_:didReceive:withCompletionHandler:)
,所以响应处理依旧写在此方法里
switch response.actionIdentifier {
case notificationActionIdentifierScan:
return
case notificationActionIdentifierReply:
// 获取回复内容并处理
if let _ = (response as? UNTextInputNotificationResponse)?.userText {
}
return
case notificationActionIdentifierDismiss:
return
default:
return
}