iOS 10 的一个重要更新-自定义的通知界面

续上篇,在简单闹钟的例子上,在通知界面上显示图片动画,并用通知关联的按钮更新通知界面。介绍 iOS 10 通知 API 的扩展:自定义通知显示界面。swift

 

新框架能够统一处理本地通知和远程推送,同时增长了一些新 API 来控制等待中和已发出的通知。api

 

以上这些都很棒,不过苹果还在通知方面更进一步,让开发者能添加一个自定义的通知界面,用户收到通知以后能够选择查看这个自定义界面。要实现这个功能,须要添加一个单独的 UserNotificationsUI 框架。这个框架的 API 特别简单,只含有一个公共的 protocol:UNNotificationContentExtension (https://developer.apple.com/reference/usernotificationsui/unnotificationcontentextension)。数组

 

工程架构

 

咱们的样例工程是在上一篇文章的闹钟 app 基础上,增长了一个炫酷的自定义通知界面。经过这个界面,用户能够不用切换到闹钟 app 就能直接取消通知。先来看下效果:app

自定义通知界面效果框架

 

跟全部 Day by Day 系列文章同样,工程源码放在了 Github 上。ide

 

建立 Extension动画

 

iOS 10 的许多旗舰功能都是创建在苹果的 Extension 架构上的。前面的系列文章 Xcode 插件 和 iMessage 插件 都是如此。而自定义通知界面也是用一样的方法实现的。ui

 

首先,咱们要给闹钟 app 的工程加一个新的 target。在下面这个选择 target 模板的界面,选择 Notification Content。而后随便起个名字,我用的是 NagMeContentExtension。spa

 

选择 target 模板

 

你可能会注意到,除了默认的Info.plist以外,这个 extension 还包含另外两个文件:

 

  • MainInterface.storyboard : 咱们把自定义通知界面的 UI 画在这里

  • NotificationViewController.swift : 一个 UIViewController 的子类,这就是自定义界面的 ViewController,咱们经过这个类来管理自定义的界面。

 

把 Extension 与通知 category 关联起来

 

如今工程设置好了,咱们须要让系统知道,是哪一个通知要展现这个界面。不知道你记不记得,上一篇文章讲过,一个 category 就是一个很简单的对象(参考 UNNotificationCategory),里面定义了你的 app 支持哪些类型的通知,以及每种通知关联了什么操做——就是用户把通知展开的时候,通知下面出现的那些操做按钮。

 

具体实现这一步,须要打开 extension 的 Info.plist,展开 NSExtensionAttributes Dictionary,把下面 UNNotificationExtensionCategory 这个键对应的值改成通知 category 的名字(“reminder”)。注意,这个值既能够填一个 string ,也能够填一个 string 数组,若是想让多个通知 category 共用一个 extension 界面就能够填 string 数组。

 

Info.plist

 

如今把工程 Build、Run 一下,咱们能够看到一个比默认的通知弹框更有意思一点的界面。

 

extension 的默认界面

 

管用了!如今用的是 extension 默认的 MainInterface.storyboard 界面,而后是 NotificationViewController 里的模板代码在更新界面上的 label。不过这个界面仍是有几点须要改进的地方。首先,通知的内容(”Walk Dog!!”)在 extension 的界面上和 DefaultContent 区域重复出现了两次。咱们先把这个重复的去掉吧!

 

去掉 DefaultContent

 

很简单,只需在 Info.plist 文件里的 NSExtensionAttributes 下面增长一个 key ,UNNotificationExtensionDefaultContentHidden,而后值设为 YES,就不会显示 DefaultContent 了。

去掉 default content 以后

 

好,下面咱们来写自定义的界面吧。

 

自定义的通知界面

 

切换到 MainInterface.storyboard,加上 UI 控件。加一个 label 描述提醒的事项,加一个小喇叭的图片。加完以后,只需拖几个 IBOutlets 出来,就大功告成啦!

 

收到通知的时候,咱们要更新 label 上的文本,同时摇晃小喇叭的图片——用这种粗暴的方式吸引用户的注意力。要实现这些功能,须要在 NotificationViewController 里进行一些修改。咱们的 viewController 实现了 UNNotificationContentExtension 这个 protocol,下面用到的就是这个 protocol 中定义的方法:

 

func didReceive(_ notification: UNNotification) {

  label.text = "Reminder: \(notification.request.content.body)"

  speakerLabel.shake() // 具体实现下载源码能够看到

}

 

这个方法就是收到通知以后,根据通知内容来配置通知界面的指定方法。

 

初步的通知界面

 

看起来还不错,可是中间有一大段空白,看上去不大美观。

 

幸运的是,要解决这个问题只需加 Info.plist 里再加一个 key UNNotificationExtensionInitialContentSizeRatio,它定义了自定义通知界面的高宽比。这个值可能须要多试几回来调整,对于咱们目前的状况取 0.5 就比较合适了(当宽度是 300 的时候,高度是 150)。

 

调整高宽比以后的界面

 

NotificationViewController 就是一个单纯的 UIViewController 的子类,用起来跟你日常在主 app 里用普通的 viewController 是同样的。惟一的不一样点在于它的 userInteraction 是 disabled 的,意思是彻底没法接收到用户的点击、触摸事件。因此有部分控件是用不了的,好比 UIScrollView、UIButton 等。

 

接受用户操做

 

自定义的界面咱们画出来了,可是还有一点要改进:点击 “Cancel” 按钮,只会让用户切回到闹钟 app,这一步有点多余。

 

在上一篇文章咱们讲了怎么给通知加上操做按钮:通知出现时能够进行的每一项操做都是一个 UNNotificationAction,关联在通知 category 上。更详细的介绍能够参考官方文档。

 

而 UNNotificationContentExtension 这个 protocol 提供了另外一个处理点击事件的方法:didReceive(_:completionHandler:)。咱们就用这个方法,把小喇叭的 icon 改为红线划掉的小喇叭,而后把通知从 UNNotificationCenter 中移除。

 

func didReceive(_ response: UNNotificationResponse,

                completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {

 

  if response.actionIdentifier == "cancel" {

    let request = response.notification.request

 

    let identifiers = [request.identifier]

 

    // 移除后续的通知

    UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)

 

    // 移除以前的通知,不在用户的通知列表里占地方了

    UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)

 

    // 通知取消的视觉反馈

    speakerLabel.text = "🔇"

    speakerLabel.cancelShake()

 

    completion(.doNotDismiss)

  }

  else {

    completion(.dismiss)

  }

}

 

相关的通知都移除了,UI 也更新了,接下来咱们须要告诉系统该怎么处置这个通知界面。由于咱们想让用户看到被划掉的小喇叭,获得通知被取消的视觉反馈,因此要把通知留在屏幕上,所以回调里传入 UNNotificationContentExtensionResponseOption 的一个取值 .doNotDismiss。

 

取消通知

 

既然要用这个方法处理点击,就得处理好每个按钮事件。在这个例子里,咱们只有一个“Cancel”按钮。然而,若是还有别的按钮,它们的点击事件也须要处理好:要么也在 extension 工程的这个方法里处理,要么回调传 UNNotificationContentExtensionResponseOption.dismissAndForwardAction,传给主 app 去处理。

 

扩展阅读

 

UserNotificationsUI 这个框架并无什么惊天动地的突破,但它能让用户与 app 的交互更便捷。用户能够直接对通知进行操做,不用再切换到发出通知的 app 了;甚至通知界面的 UI 也能动态改变,来更好地反馈用户操做的结果。

 

关于通知的其余“高级”特性,我推荐看看 WWDC 2016 的演讲视频。这场视频中,演讲者给出了几个苹果官方 app 自定义通知界面的例子,好比接收日程邀请。

相关文章
相关标签/搜索