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 = "?"

    speakerLabel.cancelShake()

 

    completion(.doNotDismiss)

  }

  else {

    completion(.dismiss)

  }

}

 

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

 

取消通知

 

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

 

扩展阅读

 

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

 

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

 

转载于:https://www.cnblogs.com/fengmin/p/6006688.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402754.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[USACO 1.3.3]Calf Flac

o(︶︿︶)o 烦躁,看了半天没看懂这个O(n)的回文串算法是什么东西,直接套上模板就交了。然后AC了 题目: Description 据说如果你给无限只母牛和无限台巨型便携式电脑(有非常大的键盘),那么母牛们会制造出世上最棒的回文。你的工作就是去这些牛…

[libuv] libuv学习

From: https://www.mobibrw.com/2016/3490 libuv 是重写了下libev,封装了windows和unix的差异性。 libuv的特点 非阻塞TCP套接字 socket? 非阻塞命名管道 UDP 定时器 子进程 fork? 通过 uv_getaddrinfo实现异步DNS 异步文件系统API uv_fs_* 高分辨率时…

elemnt的Table 表格使用注意事项

elemnt的Table 表格使用注意事项 1、修改数据 <template slot-scope"scope"><el-button size"mini" clickupdatePassword(scope.row)>修改密码</el-button><el-button size"mini" clickresetPassword(scope.row)>重置密…

px、em、rem、vm、vw 、vh、vmin 、vmax区分

Px表示“绝对尺寸”&#xff08;并非真正的绝对&#xff09;&#xff0c;实际上就是css中定义的像素&#xff08;此像素与设备的物理像素有一定的区别&#xff0c;后续详细说明见文末说明1&#xff09;&#xff0c;利用px设置字体大小及元素宽高等比较稳定和精确。Px的缺点是其…

Class.forName()用法详解

Class.forName()用法详解 标签&#xff1a; classjvmjdbc数据库documentationjava2012-03-29 09:39 40414人阅读 评论(8) 收藏 举报分类&#xff1a;Java考古学&#xff08;74&#xff09; 主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要…

[poco] 访问数据库

From: https://blog.csdn.net/hl2015222050145/article/details/52335422?utm_sourceblogxgwz6 poco访问数据基本步骤&#xff1a; a. 创建会话(session) b. 从DB中读写数据 c. 使用statements d. 使用容器&#xff08;Collection&#xff09; (数据&#xff0c;集合...) …

应对不良网络文化的技术之一——网络信息抽取技术

1 引言 2008年1月17日&#xff0c;中国互联网络信息中心(CNNIC)发布了《第21次中国互联网络发展状况统计报告》[1]&#xff0c;报告显示&#xff1a; (1) 截至2007年12月&#xff0c;网民数已增至2.1亿人。中国网民数增长迅速&#xff0c;比2007年6月增加4800万人&…

最安全的js类型检测

众所周知js内置的类型检测机制不可靠&#xff0c;比如typeof操作符&#xff0c;对于正则和数组检测时返回值都是object&#xff0c; 而使用instanceof检测类型时&#xff0c;虽然可以对正则和数组正常验证&#xff0c;但验证undefined会报错&#xff0c;还有对于Symbol无法验证…

HBuilder完成webApp入门(2)

一、HBuilder的下载地址&#xff1a;http://www.dcloud.io/&#xff0c;点击那个“DownLoad”就可以 了 二、假设一切顺利&#xff0c;启动HBuilder后&#xff0c;大家会看到如下的界面 点击新建移动APP&#xff1a; 接下来就会弹出一个选择模板的对话框&#xff1a; 默认的模板…

js防篡改对象之不可扩展对象

const person {name: 啦啦德玛西亚}console.log(Object.isExtensible(person))//true//使用Object.preventExtensions()可以将对象修改为不可扩展对象&#xff0c;无法再给对象添加属性和方法Object.preventExtensions(person)//使用Object.isExtensible()方法可以确定对象是否…

高可用集群 heartbeatv1实例

——————— 高可用集群的简单配置 ————————地址规划 主节点&#xff1a;HA1 172.16.21.13 hostname node2.magedu.com备节点&#xff1a; HA2 172.16.21.14 hostname node1.magedu.comVIP 172.16.21.9前提工作1&#xff0c;配置主机名 hostname保证uname …

你知道“拉黑”、“关注”、“点赞”、“转发”、“分享到朋友圈”等英语咋说吗?

From: https://www.sohu.com/a/220161051_559507 “分享到朋友圈”等英语咋说吗&#xff1f; Mini apps 小程序 小程序”&#xff08;mini apps&#xff09;是一个不需要下载安装就可使用的应用&#xff08;apps that can be accessed without downloading&#xff09;&#x…

C#进化史

C#进化史 C#进化史从数据类型看C#演化C# 1写的产品类C# 强类型集合——解决限制1和2c# 自动属性——解决限制3c# 4 命名实参C# 1~C# 4的演变历程排序C# 1——提供一个IComparer实现C# 2——泛型比较器C# 3——Lambda表达式、扩展方法C# 1~C# 3简化排序的历程查询集合C# 1——循…

防篡改对象之密封对象

const person {name: 啦啦德玛西亚}console.log(Object.isExtensible(person))// trueconsole.log(Object.isSealed(person))// false// 使用Object.seal()将对象修改为密封对象// 密封对象不可扩展&#xff0c;而且已有成员的[[Configurable]]特性将被设置为false// 意味着不…

配套自测连载(三)

接上期(答案已给出)本期是专门针对《深入理解计算机网络》图书第4章而编写的10道计算机网络体系架构中的物理层技术自测题&#xff0c;可以检验你对本章的学习效果。把你的答案直接写在评论中即可&#xff0c;笔者将在每期发表10天后给出正确答案。本书是国内最通俗、最系统的计…

[json] JSON for Modern C++

有幸能接触到这个&#xff0c;这是我遇到的使用最方便的json了&#xff0c;效率没研究过&#xff01; 简单了使用了下&#xff0c;感觉非常好用&#xff0c;记录下&#xff1a; 要使用这个json&#xff0c;只需要使用json.hpp就行&#xff0c;放入自己的工程里&#xff0c;但…

面向对象的需求分析方法

面向对象的需求分析方法 面向对象的需求分析方法的核心是利用面向对象的概念和方法为软件需求建造模型。它包含面向对象风格的图形语言机制和用于指导需求分析的面向对象方法学。 面向对象的思想最初起源于 20世纪 60年代中期的仿真程序设计语言Simula67。20世纪80年代初出现的…

js防篡改对象之冻结对象

const person {name: 啦啦德玛西亚,_job: 无业}Object.defineProperty(person, job, {get: function() {return this._job},set: function(newValue) {this._job newValue}})console.log(Object.isFrozen(person))// false//使用Object.freeze()将对象修改为冻结对象&#xf…

libinject的编译

libinject是一个Android进程注入实例&#xff0c;其下载地址为&#xff1a;http://download.csdn.net/download/ljhzbljhzb/3680780 libinject的编译需要NDK开发环境&#xff0c;在NDK安装成功之后&#xff0c;可以先将其自带的实例中的HelloJni导入到eclipse中&#xff0c;编译…

Boost - 序列化 (Serialization)

From: https://blog.csdn.net/zj510/article/details/8105408 程序开发中&#xff0c;序列化是经常需要用到的。像一些相对高级语言&#xff0c;比如JAVA, C#都已经很好的支持了序列化&#xff0c;那么C呢&#xff1f;当然一个比较好的选择就是用Boost&#xff0c;这个号称C准…