iOS 10 的一个重要更新-自定义的通知界面
续上篇,在简单闹钟的例子上,在通知界面上显示图片动画,并用通知关联的按钮更新通知界面。介绍 iOS 10 通知 API 的扩展:自定义通知显示界面。
新框架可以统一处理本地通知和远程推送,同时增加了一些新 API 来控制等待中和已发出的通知。
以上这些都很棒,不过苹果还在通知方面更进一步,让开发者能添加一个自定义的通知界面,用户收到通知之后可以选择查看这个自定义界面。要实现这个功能,需要添加一个单独的 UserNotificationsUI 框架。这个框架的 API 特别简单,只含有一个公共的 protocol:UNNotificationContentExtension (https://developer.apple.com/reference/usernotificationsui/unnotificationcontentextension)。
工程
我们的样例工程是在上一篇文章的闹钟 app 基础上,增加了一个炫酷的自定义通知界面。通过这个界面,用户可以不用切换到闹钟 app 就能直接取消通知。先来看下效果:

自定义通知界面效果
跟所有 Day by Day 系列文章一样,工程源码放在了 Github 上。
创建 Extension
iOS 10 的许多旗舰功能都是建立在苹果的 Extension 架构上的。前面的系列文章 Xcode 插件 和 iMessage 插件 都是如此。而自定义通知界面也是用同样的方法实现的。
首先,我们要给闹钟 app 的工程加一个新的 target。在下面这个选择 target 模板的界面,选择 Notification Content。然后随便起个名字,我用的是 NagMeContentExtension。

选择 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 = "
iOS 10 的一个重要更新-自定义的通知界面的更多相关文章
- iOS 10 的一个重要更新-新的通知推送 API
iOS 10 最重要的变化可能就是通知 API 的重构了.本文用一个简单闹钟的例子介绍了 User Notification 的 API 变化和新功能. 简介 很久以前,开发者就可以在 iOS 里预约 ...
- iOS 10 的一个重要更新-开发 iMessage 的第三方插件
苹果官方的 Messages 在 iOS 10 推出了非常重大的更新,可能主要是想从其他 IM 巨头手里抢点市场份额回来,包括 Facebook Messenger, Wechat 和 Snapcha ...
- iOS 10 的一个重要更新-线程竞态检测工具 Thread Sanitizer
本文介绍了 Xcode 8 的新出的多线程调试工具 Thread Sanitizer,可以在 app 运行时发现线程竞态. 想想一下,你的 app 已经近乎大功告成:它经过精良的打磨,单元测试全覆盖. ...
- iOS 10 的一个重要更新-用 UIViewPropertyAnimator 编写动画
曾经的黑暗年代 用基于 block 的 UIView animation 来编写 view 属性(frame, transform 等等)变化的动画非常简单.只需要短短几行代码: view.alpha ...
- 【转】具透 | 你可能不知道,iOS 10 有一个中国「特供」的联网权限功能
9 月底,苹果正式在北京成立了苹果中国研发中心.近几年,我们也在每年更新的 iOS 系统中不断看到,苹果对中国市场的关照.从早前的九宫格输入法,到最近的骚扰电话拦截,都照顾了国内用户的需求. 在 iO ...
- [iOS 10 day by day] Day 1:开发 iMessage 的第三方插件
本文介绍了 iOS 10 的一个重要更新:Messages 应用支持第三方插件了.作者用一个小游戏作为例子,说明了插件开发从建工程开始,到绘制界面.收发消息的全过程. <iOS 10 day b ...
- iOS 10 消息推送(UserNotifications)秘籍总结(一)
前言 之前说会单独整理消息通知的内容,但是因为工(就)作(是)的(很)事(懒)没有更新文章,违背了自己的学习的初衷.因为互联网一定要有危机意识,说不定眼一睁,我们就out丢了饭碗. 图片来源网络.jp ...
- 第三十二篇、iOS 10开发
1.语音识别 苹果官方在文档中新增了API Speech,那么在以前我们处理语音识别非常的繁琐甚至很多时候可能需要借助于第三方框架处理,那么苹果推出了这个后,我们以后处理起来就非常的方便了,spe ...
- zTree更新自定义标签>>>
zTree>>>>>>>>>>>>>>>>>>>>>>>> ...
随机推荐
- Java方法containsAll学习
有时候我们需要判断B链表是不是A链表的子集,我们可以使用A.containsAll(B)来判断,当返回值是true的时候就表明B链表是A链表的子集,当返回值是false时候就表明B链表不是A链表的子集 ...
- Pandas python
原文: https://github.com/catalystfrank/Python4DataScience.CH 和大熊猫们(Pandas)一起游戏吧! Pandas是Python的一个 ...
- 迁移Windows下的MySQL时字符乱码问题
我们常常会直接复制一份MySQL的Data文件夹到新的环境下,正常情况下重新启动MySQL就可以使用.但有时也会遇到些问题: 1.程序訪问时提示找不到表,实际表已经存在 这样的情况是因为数据库全部者可 ...
- artTemplate 原生 js 模板语法版
在页面中引用模板引擎: <script src="dist/template-native.js"></script> 下载 表达式 <% 与 %&g ...
- selenium之 chromedriver与chrome版本映射表(更新至v2.38)
https://blog.csdn.net/huilan_same/article/details/51896672 看到网上基本没有最新的chromedriver与chrome的对应关系表,便兴起整 ...
- PHP开发框架比较
PHP开发框架比较 Laravel 是一个简单优雅的 PHP WEB 开发框架,将你从意大利面条式的代码中解放出来.通过简单.优雅.表达式语法开发出很棒的 WEB应用!但是通过使用我们发现Larave ...
- JavaScript 作用域和闭包——另一个角度:扩展你对作用域和闭包的认识【翻译+整理】
原文地址 --这篇文章有点意思,可以扩展你对作用域和闭包的认识. 本文内容 背景 作用域 闭包 臭名昭著的循环问题 自调用函数(匿名函数) 其他 我认为,尝试向别人解释 JavaScript 作用域和 ...
- 牛客网-《剑指offer》-二进制中1的个数
题目:http://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8 C++ 负数需要特殊处理,因为负数右移会补1(符号位) cla ...
- 牛客网-《剑指offer》-二维数组中的查找
题目:http://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e C++ class Solution { public: bo ...
- postman添加cookie
检索cookie: 1.启动拦截器(需安装Postman Interceptor) 2.在测试部分,你可以使用responseCookies对象,他将返回一个cookie对象的数组.使用postman ...