此篇我们接着Session 707接着讲,有兴趣的可以查看 : http://xiaolu520.com/2017/06/08/WWDC2016-Session707%EF%BC%88UNNotifications%EF%BC%89/

概览
本篇主要是对
UNNotification框架的扩展,以及一些高级功能的实现
主要讲三个部分

- 通知的用户界面多种多样。
- 通知可以支持附件
- 用户针对界面、交互的自定义
通知用户界面的多种多样
iOS 10新推出的通知框架可以实现十分丰富的推送效果,可以实现图片、视频、音频、Gif动态图片的效果,同时可以实现快捷回复,自定义用户交互等一系列酷炫的效果。此处就不上图了。
通知的媒体附件

上一篇我们了解了一个全新的十分重要的概念
Service Extension。此篇会使用Service Extension来完成一些重要的功能。
众所周知,iOS的通知在改版之后支持最大的推送容量仅仅才4KB,我们想依靠推送通知实现音视频的展示显然是不切实际的,这时候我们就要用到 Service Extension。
媒体附件支持本地通知和远程通知,可以支持图片、视频、音频、Gif动态图片,而这些媒体资源会在系统内存中单独开辟出的一块儿空间存放,无需开发者进行管理,需要使用的时候,可以申请读取。

在通知推送到用户设备之前,我们需要标明这个推送的内容是可变的,同时将附件以链接的形式给my-attachment属性赋值,代码如下:
{
aps: {
alert: { … },
mutable-content: 1
}
my-attachment: "https://example.com/photo.jpg"
}
在Service Extension中,可以对附件的链接进行下载操作。这样就可以实现通过Extension实现下载操作。

Public class notificationService:UNNotificationServiceExtension {
override public func didreceive(request:UNNotificationRequest,withContentHandler contentHandler: (UNNotificationContent) -> Void){
let fileURL = "www.baidu.com"
let attachment = UNNotificationAttachment(identifier: "image" ,url: fileURL,options: nil)
let content = request.content.mutableCopy as! UNMutableNotificationContent
content.attachments = [ attachment ]
contentHandler(content)
}
通过以上的步骤即可实现,通过Service Extension下载附件并显示出来的功能。
自定义通知样式

那么针对通知的样式,我们可能会有这样或者那样的需求,我们希望通知的风格和我们的App风格一致,那么这个时候,我们就要自定义通知的
UI样式。
想实现通知的自定义样式,我们会需要
- 通知内容的扩展(Notification content extension)
- 自定义的View
- 无需用户交互
- 针对通知可以触发相应的动作
首先创建一个Notification Content


通过代码,我们就知道这个
Notification Extension继承自UIViewController,其实就是VC那一层。我们一共会得到三个文件。一个VC文件,一个StoryBoard的图形化界面文件还有一个info.plist的配置文件。
VC文件中只有一个遵守UINotificationContentExtension协议的didReceive方法是必须实现的,这个方法随着通知被发送到Extension,当用户展开通知时需要更新UI,随视图控制器的生命周期方法被一起调用,通过这个方法我们获取到具体的通知,得到通知的内容以及其他信息,从而也就可以进行接下来的操作.
为了让Notification Extension知道具体是哪个通知该被扩展,你需要在info.plist文件中,对Notification Extension Category进行注册,此时注册所用的Identifier,应该和你发送通知时所用的identifier一致,这样就将这个Extension和你的Notification绑定了,当然扩展也可以关联多个类型。
如下图:

在自定义通知的过程中,会碰到一些问题:

例如,显示范围过大,或者通知会显示重复的信息。
要解决通知会显示重复的信息,这个只需要在info.plist中将默认的属性值修改了就可以。

而为了解决显示区域过大,我们要修改显示区域的范围,通过PreferedContentSize可以做到,我们可以按照自己的需要设定一个范围。但是这样还会有另外一个问题,就是通知默认是按照大尺寸出现的,出现后的一瞬间再按照PreferedContentSize所给的尺寸去更新UI,为了避免出现这种动画,只需修改另一个属性UNNotificationExtensionDefaultContentHidden。

UNNotificationExtensionDefaultContentHidden这个属性,可以保证通知界面一开始不出现,知道改为合适的尺寸直接出现,这样做就避免了通知出现一瞬间,还要更新UI。
PreferedContentSize我们需要在代理方法中自己用代码设定,也可以在info.plist利用UNNotificationExtensionInitialContentSizeRatio设置区域。
Public class notificationService:UNNotificationServiceExtension {
override func viewDidLoad() { super.viewDidLoad() {
let size = view.bounds.size preferredContentSize = CGSize(width: size.width, height: size.width / 2)
}
override public func didreceive(request:UNNotificationRequest,withContentHandler contentHandler: (UNNotificationContent) -> Void){
}

展示通知的媒体附件
我们知道通知的媒体附件被系统单独的开辟出一块儿内存空间所管理,所以当我们想要使用的时候,我们要申请读取,访问结束后要将空间关闭
class NotificationViewController: UIViewController, UNNotificationContentExtension {
@IBOutlet var eventImage: UIImageView!
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
if let attachment = content.attachments.first {
// 使用时申请访问
if attachment.url.startAccessingSecurityScopedResource()
{
eventImage.image = UIImage(contentsOfFile: attachment.url.path!)
// 用完了需要关闭
attachment.url.stopAccessingSecurityScopedResource()
}
}
}
}
通过通知我们可以与用户互动,用户点击了不同的选项,我们可以有不同的行为,例如接受或者拒绝。
class NotificationViewController: UIViewController, UNNotificationContentExtension
{
func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) {
server.postEventResponse(response.actionIdentifier)
{
if response.actionIdentifier == "accept"
{
eventResponse.text = "Going!"
eventResponse.textColor = UIColor.green()
}
else if response.actionIdentifier == "decline"
{
eventResponse.text = "Not going :("
eventResponse.textColor = UIColor.red()
}
done(.dismiss)
}
}
}
同时也允许用户进行输入操作,同时还增加了PlaceHolder
private func makeEventExtensionCategory() -> UNNotificationCategory {
let commentAction = UNTextInputNotificationAction( identifier: "comment",
title: "Comment",
options: [],
textInputButtonTitle: "Send",
textInputPlaceholder: "Type here…")
return UNNotificationCategory(identifier: "event-invite",
actions: [ acceptAction, declineAction, commentAction ],
minimalActions: [ acceptAction, declineAction ],
intentIdentifiers: [],
options: [])
}
// 点击后
class NotificationViewController: UIViewController, UNNotificationContentExtension {
func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) {
if let textResponse = response as? UNTextInputNotificationResponse
{
server.send(textResponse.userText)
{
done(.dismiss)
}
}
}
}
还可以自定义inputView
class NotificationViewController: UIViewController, UNNotificationContentExtension
// 判断能否成为第一响应者
override func canBecomeFirstResponder() -> Bool {
return true
}
// 重写inputView
override var inputAccessoryView: UIView {
get {
return inputView
}
}
func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "comment"
{
becomeFirstResponder()
textField.becomeFirstResponder()
}
}
}