[iOS 10 day by day] Day 6:自定义的通知界面

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

《iOS 10 day by day》是 shinobicontrols 公司编写的系列博客,介绍开发者需要了解的 iOS 10 新特性,每周更新。本系列翻译(文集地址)已取得官方授权。目录点此。仓薯翻译,欢迎指正:)

Shinobicontrols 为 iOS 和 Android 开发者提供高性能、响应式的 UI 控件 SDK,尤其是图表方面的控件。 官网 : shinobicontrols.com twitter : @shinobicontrols

我们在 Day 5 中介绍了新的 UserNotifications 框架。新框架可以统一处理本地通知和远程推送,同时增加了一些新 API 来控制等待中和已发出的通知。

以上这些都很棒,不过苹果还在通知方面更进一步,让开发者能添加一个自定义的通知界面,用户收到通知之后可以选择查看这个自定义界面。要实现这个功能,需要添加一个单独的 UserNotificationsUI 框架。这个框架的 API 特别简单,只含有一个公共的 protocol: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 = "🔇" 
    speakerLabel.cancelShake()

    completion(.doNotDismiss)
  }
  else {
    completion(.dismiss)
  }
}

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

取消通知

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

扩展阅读

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

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

原文地址:iOS 10 Day by Day :: Day 6 :: Notification Content Extensions

原作者:Sam Burnstone @sam_burnstone

ShinobiControls 官网:ShinobiControls.com twitter : @shinobicontrols

文集地址:iOS 10 day by day 仓薯翻译

本文地址://www.greatytc.com/p/001a5ab81808

译者:戴仓薯

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,438评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 无言独上西楼,月如钩。 寂寞梧桐深院锁清秋。 剪不断理还乱,是离愁。 别是一番滋味,在心头。
    半蓑烟雨任平生阅读 323评论 0 0
  • 懂小姐步不姓懂,家里也有没有草原。懂小姐是我多年的好友,跟她说什么她都会说“我懂啊”,于是大家都叫她,懂小姐。 刚...
    大栗子阅读 791评论 3 10