ununusernotificationnserviceextension与ios10以下的系统兼容是怎么样的

作为一名iOS开发者,密切关注苹果的最新动态是一名iOS开发者必备技能。关于iOS10中的新特性介绍的也比较多,今天小编推荐董铂然博客中关于iOS10新增Api的功能介绍。干货多多,不容错过!
本文主要是一些对iOS新功能的探索,之前发现博客里关于iOS新功能的分析大多是过于概括,每个功能几句话,无法了解到具体的功能。
所以本次的探索是基于Api层面,着重看一些具体用法所做的笔记,本来想分别画个类图来着,后来还是觉得太耗时而放弃。
如果不想把苹果iOSSDK里的.h文件都摆出来详细读,那看这篇笔记应该是个不错的选择。
本文以文档的目录为基础,提到的功能模块有:
Proactive Suggestions
Integrating with the Messages App
User Notifications
Speech Recognition
Wide Color
Adapting to the True Tone Display
App Search Enhancements
Widget Enhancements
Security and Privacy Enhancements
还没有详细探索的模块是
CallKit (工作职能和这里联系不多,还不care这里)
Apple Pay Enhancements (工作职能和这里联系不多,还不care这里)
SiriKit的功能非常强大,支持音频、视频、消息发送接收、搜索照片、预订行程、管理锻炼等等。
在用到此服务时,siri会发送Intent对象,里面包括用户的请求和各种数据,可以对这个intent处理选择适当的响应。
这个功能主要是看这两个头文件(#import&Intents/Intents.h&、#import &IntentsUI/IntentsUI.h&)所涵盖到的类太多,前期先略过了,之后等iOS10可以推送更新了再做进一步调研。
Proactive Suggestions 系统预先建议
背景就是iOS9的时候系统给予的主动建议会通过:Spolight搜索,Safari搜索,Handoff,或者siri建议。
在iOS10之后新增了,键盘QuickType建议,地图,车载娱乐,应用切换,siri交互,锁屏播放。
比如你正在一个应用里看一个酒店,可以使用mapitem属性保存正在查看的这个酒店的位置,然后你切换旅行或地图App时这个位置可以自动提供使用。
如果你需要这样利用系统来共享一个位置,那你需要指定这个位置的经纬度,地名,电话等属性 来便于siri的直接调起。
文档中还列出了几种场景
1、在输入框(UITextFiled)输入时,可以指定一下这个输入框的类型,以便系统可以分析出用户的语义。
是电话类型就建议一些电话,是地址类型就建议一些地址。
看下头文件(#import &UIKit/UITextInputTraits.h&)可指定的类型 就是这个新增的textContentType字段,里面有很多种类型可选。
2.如果是视频类App可以使用MPPlayableContentManager(#import &MediaPlayer/MPPlayableContentManager.h&)看了下,新增了一个属性nowPlayingIdentifiers,苹果的意思应该是只要你以前是用这个多媒体类播放音乐的我就可以让你在锁屏页面交互,需要配置在这个数组里。
3.如果是出行类app可以使用MKDirectionsRequest。(#import &MapKit/MKDirectionsRequest.h& )这个类里几乎没有新增api,iOS10就新增了一个枚举,而且看上去就是个默认值。
如果要使用此功能,需要配置在info.plist文件里 新增key – value ,MKDirectionsApplicationSupportedModes – MKDirectionsModeRideShare。
User Notifications 用户通知
总体的意思就是支持了很多用户定义的通知,并且可以捕捉到各个通知状态的回调。
以往通知的概念是:大家想接收的都提前做好准备,然后一下全量分发,没收到也不管了,也不关心发送者。
现在用户通知做成了和网络请求有点像 一个先发request再得到response的流程,甚至封装了error,可以在各个状态的方法中做一些额外操作,并且也能取到一些字段,如发送者等。
此功能的头文件入口在#import &UserNotifications/UserNotifications.h&
1.NSString+UserNotifications.h
有一个方法 localizedUserNotificationStringForKey: arguments: (提供该通知被呈现时的本地化字符串),猜测下面的类有很多initWithIdentifier的,他们的indentifier就是这个。
2.UNError.h
有一个属性UNErrorDomain 和一个枚举 UNErrorCode,顾名思义。
3.UNNotification.h
里面有两个属性,date日期 和 request,这个request是上面“8”的类型UNNotificationRequest,点进去看了下比较清晰,有identifier标识,content内容,trigger触发条件, 和带上这三个东西的初始化方法。
其中内容 和 触发条件这两个属性,分别是上面“7” 和 “12”的类型,这个下面再谈。
4.UNNotificationAction.h
这个类突出的是一个通知的动作,有identifier,title,options(枚举,就是通知当前的权限,允许?拒绝?前台时允许?)属性。
然后就是带上这三个东西的初始化方法。 然后比较费解的就是下面有一个子类UNTextInputNotificationAction ,这个子类有两个额外属性, 按钮title,和文本框placeholder, 为什么会是这两个属性?
莫非是点击通知后下拉出的快速回复,有一个输入框和一个按钮。
5.UNNotificationAttachment.h
这个里面就是URL(资源url属性),type(附件类型)。 然后是带上这两个属性的初始化方法。
下面声明了几个字符串常量,暂时还不知道具体用在哪里,typeHint,hiddenKey,clippingRectKey,TimeKey。
6.UNNotificationCategory.h
有indentifier属性,actions(里面是数组),minimalActions(最重要的数组,就是只能给你两个位置显示你显示哪两个,这么个意思),intentIdentifiers属性(应该是和上面的动作数组关联的吧),options(权限相关,无?允许自定义关闭?允许车载系统交互?)。
最后就是把这些都带上的init方法。
猜测这个类之所以取名叫category应该是,在某个地方展示通知的时候会把所有通知一一分类。
然后每个类别的通知可能最多只能让你展示几个,如果不做限制应该会展示全部通知,如果权限设置的是允许自定义关闭那可能就是支持类似一键清除的操作。
7.UNNotificationContent.h
消息的内容,一看就能知道应该是一个类似于Entity的东西,里面装有大量的属性:
attachments(可选的附件集合),badge(小红点数量),body,categoryIndentifier,launchImageName(从消息里点开的应用程序应该能看到启动图对吧),subtitle,threadIdentifier(与request关联),title,userInfo,sound这个是“11”的类型。
应该是同时来时的声音,点开“11”看一下 ,就俩方法,defaultSound,soundNamed: 自定义声音,都在~/Library/Sounds 目录下。
恩再回到刚才那个content类里面有个子类UNMutableNotificationContent,属性和父类相同,只不过是子类的属性都可以修改了,父类的那些属性都是readonly的。
8.UNNotificationRequest.h 上面第3条说过了
9.UNNotificationResponse.h
有action,也有request,那也就有response,这里面有两个属性,notification,actionIdentifier 响应里就这俩破玩意。
然后有个子类UNTextInputNotificationResponse, 这里面就一个属性userText ,看命名很好猜,应该就是前面说的那个有输入框里输入的内容。
10.UNNotificationSettings.h
这个类里就是一些设置了,有一个枚举说的是有没有权限,一个枚举说的是不支持?禁用?启用?。
然后下面一大波属性,小红点设置,声音设置,弹窗设置等等 都是这个枚举类型, 最后还有个alertStyle属性(枚举,None?Banner?alert?)。
11.sound前面第7条说过了
12.UNNotificationTrigger.h
有一个属性 repeats(是否重复发通知)。
下面有四个子类,push通知触发, 时间通知触发,日历通知触发,地区通知触发, 时间的有timeInterval属性, 日历的有dateComponents属性。
然后时间和日历的子类都有nextTriggerDate 方法。
13.UNUserNotificationCenter.h
这里面东西多到吐了,同学你记得NSNotificationCenter么? 需要提一点的就是以前的通知中心有个方法[NSNotificationCenter defaultCenter], 这里是[UNUserNotificationCenter currentNotificationCenter], 提醒一下到时候别说敲不出来。
方法大多是一些remove,add,get等操作, 还有2个代理方法:通知将要发出去时调用,收到通知的response后调用。
14.UNNotificationServiceExtension.h
里面有两个方法,收到通知的请求后调用, 系统将要销毁时调用。
通知里面有UI相关的类 #import &UserNotificationsUI/UserNotificationsUI.h&
这个类里面就一个文件, 而且方法比较单一,就是收到通知调用,和收到通知的响应调用。
其他方法也就是mediaPlay 和 mediaPause 。
和一些多媒体播放的按钮frame,color等, 这里面的作用难道仅仅就是通知来了后播放的音乐暂时暂停下,响一声通知,再播放?
具体WWDC2016上说的锁屏页面的通知样式处理的api是在下面的部件增强章节中。
Integrating with the Messages App 与系统短信 app交互
对消息额支持很大,可以模拟发消息收消息,发邮件等,提供了UI界面。
也提供了一些额外扩展的api,如表情包区域。WWDC2016上说的各种消息内的新操作在头文件中都能找到Api。
此功能的头文件入口在#import &Messages/Messages.h&
1.MSMessagesAppViewController.h
这个类应该就是苹果自己的消息界面,你可以继承他写你自己自定义的界面。
属性有:activeConversation 指的是当前的会话对象,是上面“2”这个类型,具体详细可以看下面的第“2”点、还有个是presentationStyle(外观样式,枚举类型,紧缩?扩张?)。
接下来就是方法了:requestPresentationStyle(请求消息过渡到指定的样式),dismiss消除方法。
然后就是一波生命周期方法了,每一类都有will和did,会话信息将要(已经)活跃时,将要(已经)解除活跃,将要(已经)选择信息,将要(已经)收到信息,将要(已经)开始发送,将要(已经)取消发送,将要(已经)开始过渡。
2.MSConversation.h
属性有localParticipantIdentifier(当前会话参与者生成的标识,他说只有删了App才会变 姑且理解成id是不会变的),remoteParticipantIdentifiers(远端的标识符数组),selectedMessage(选中的信息),以及4个对象方法 插入一条信息,插入一个标签,插入一段文本,插入一个附件。
这四个方法都有成功的回调。
3.MSSession.h
这个类里面是空的,解释说是用session来处理消息序列间的关系。可能就是占个位,以后估计会添加东西。
这个MSSession是后面很多参数的类型,应该就是区分消息类似于标识符的作用。
4.MSMessage.h
初始化方法是initWithSession ,没错就是上面那个MSSession。
除了初始化方法剩下的就全是属性了:session,senderParticipantIdentifier(发送者的标识符),layout布局这个是“5”这个类型,URL,shouldExpire(选yes会自动消失,用户手动选择为这条消息续命),accessibilityLabel(残疾人模式支持),error。
5.MSMessageLayout.h
这个是抽象类,里面是空的,就是个布局文件,继承自NSObject。
6.MSMessageTemplateLayout.h
继承上面那个类,看名字是模板布局后面应该会用的挺多的,里面的属性有,标题,子标题,尾部标题,尾部子标题,图片,多媒体URL,图片标题,图片子标题。
7.MSSticker.h
应该是消息上的表情包。 里面就有两个属性imageFileURL ,localizedDescription 图片和局部描述。
然后是带上这两个属性的初始化方法 initWithContentsOfFileURL: localizedDescription: 。
8.MSStickerView.h
这个类就是一个view,里面包着一个sticker,也就是包裹着上面那个装饰品的view。
提供了带上sticker的初始化方法,一个常规属性animationDuration,和三个方法startAnimating,stopAnimating,isAnimating。
9.MSStickerBrowserViewDataSource.h
细思极恐,这个类不就是wwdc2016上说的那个可以在消息下面添加自定义表情的地方么。
符合datasource的风格,里面就两个方法,numberOfStickersInStickerBrowserView: (返回一个总数),stickerBrowserView:stickerAtIndex:(返回这个索引下的内容)。
10.MSStickerBrowserView.h
上面那个是datasource,那这个就是用了上面数据源的view呗。
除了初始化方法,有两个属性 stickerSize(枚举,小,中,大),dataSource(就是上面的“9”),还有个人reloadData方法。
11.MSStickerBrowserViewController.h
上面是个view, 这个就是承载上面那个view的viewcontroller。肯定得有这个属性stickerBrowserView 和初始化方法。
这个message模块还有UI相关的api #import &MessageUI/MessageUI.h&
就是两个VC ,MFMailComposeViewController,MFMessageComposeViewController。
这两个都是继承自UINavigationController. 觉得这两个就是发邮件的编辑页面,和发消息的编辑页面。
发邮件的页面里有 canSendMail (Bool方法),set主题,set发件人,set收件人,set内容,set附件。
然后有一个代理,和代理方法,猜也能猜到 就是成功失败回调。
mailComposeController:didFinishWithResult: error: 。 那另一个消息和这差不多了就不说了。
Speech Recognition 语音识别转文字
这个类里面的Api和方法调用都非常清晰,感觉使用起来会非常方便。
真的为科大讯飞捏把汗了,科大讯飞的app(录音宝)我看过,都还没有说一段语音能直接转文字的功能,他们是直接把声音文件上传,然后24小时候再发给你转换后的结果…而且一上来就要收费。
苹果的这个功能已经做到了系统原生支持,真的厉害。
此功能的头文件入口在#import &Speech/Speech.h&
1.SFSpeechRecognitionResult.h
这个类里有三个属性:bestTranscription 就是最优的转化结果咯,是上面的“7”这个类型的。
然后再看下这个SFTranscription.h ,果然不出所料 有两个属性 一个是字符串类型formattedString 一个是数组类型的segments ,恩 前者就是转化后的字符串,后者是分割后的一个个小结果集合。
然后这个分割的一个个小结果呢又是上面“6”这个类型。
那再看一下“6” 里面的属性就是 substring, 时间戳,duration,准确性,备选答案数组,这些很清晰的东西了。
2.SFSpeechRecognitionRequest.h
这个类里东西有点多,属性taskHint,是上面“4”这个类型,点开一看就是一个枚举,用来区分你这个语音识别的请求是哪一类的 查找?确认?听写?无法识别?
接下来是两个BOOL类型的,shouldReportPartialResults(是否语音局部的一块一块也要处理?默认选false就是一句话全说完了再上传吧)。
detectMultipleUtterances(假如你说了10秒钟,只有后5秒匹配到了结果,那你前面删了还是保留?默认不删),然后是分析到的关键字数组,和标识符什么的。
然后这个request有两个子类,一个是从本地URL读取 一个声音文件去识别, 一个是默认做法用话筒和AVFoundation库接收到声音去识别,然后有几个拼接声音的API。
3.SFSpeechRecognitionTask.h
从名字就能看出来这是语音识别最重要的一个类了,里面的属性有:state这是一个枚举,说明当前状态是进行中?已完成?被取消?等等。
接下来是三个常见的 isFinishing ,isCancelled ,error 。
接下来是 isPowerAvailable (是否开启说话声音大小的监测?),peakPower(最大声音),averagePower(平均声音)。
属性就这些了,接下来就是一个协议和一波代理方法:刚刚识别出话语调用,猜测话语时调用,话说完了调用,取消时调用,等等等,你能想到的回调方法苹果应该都有的。
4.上面第2条里面说过了
5.SFSpeechRecognizer.h
和系统的那些相机权限,通讯录权限有点像, 就是现在的状态是什么?同意?拒绝?还是未选择过?
然后提供了方法让用户去选择。
然后有些属性:NSSet类型的supportedLocales(支持地区方言的集合),
available是否可用,locale当前地区 ,defaultTaskHint默认类别,request(就是上面说的请求),队列,代理。
然后有个协议和代理方法:发现用户给与的权限发生改变时调用。
6和7. 上面第1条里说过了
Wide Color 宽域颜色
文档的原话说:大多数的 core字打头的图形框架 还有AVFoundation 都大大提高了对扩展像素和宽色域色彩空间的支持。
通过图形堆栈扩展这种方式比以往支持广色域的显示设备更加容易。现在对UIKit扩展可以在sRGB的色彩空间下工作,性能更好,也可以在更广泛的色域来搭配sRGB颜色。
然后说了几个场景说建议你用sRGB吧,比如依赖于UIkit的clamp component values的应用程序,或是使用较低级别api执行自己图像处理的 都建议用sRGB吧。
然后看了下UIColor类里 到底什么是sRGB? 发现多了两个iOS10新增的api。
入职你的梦想 VS 变现你的技术IOS 10 适配 系列_0_ iOS10 UserNotifications 框架解析 - CSDN博客
IOS 10 适配 系列_0_ iOS10 UserNotifications 框架解析
iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifications.framework 来集中管理和使用 iOS 系统中通知的功能。在此基础上,Apple 还增加了撤回单条通知,更新已展示通知,中途修改通知内容,在通知中展示图片视频,自定义通知 UI 等一系列新功能,非常强大。
对于开发者来说,相较于之前版本,iOS 10 提供了一套非常易用通知处理接口,是 SDK 的一次重大重构。而之前的绝大部分通知相关 API 都已经被标为弃用 (deprecated)。
这篇文章将首先回顾一下 Notification 的发展历史和现状,然后通过一些例子来展示 iOS 10 SDK 中相应的使用方式,来说明新 SDK 中通知可以做的事情以及它们的使用方式。
您可以在 WWDC 16 的
这两个 Session 中找到详细信息;另外也不要忘了参照 以及本文的。
Notification 历史和现状
碎片化时间是移动设备用户在使用应用时的一大特点,用户希望随时拿起手机就能查看资讯,处理事务,而通知可以在重要的事件和信息发生时提醒用户。完美的通知展示可以很好地帮助用户使用应用,体现出应用的价值,进而有很大可能将用户带回应用,提高活跃度。正因如此,不论是 Apple 还是第三方开发者们,都很重视通知相关的开发工作,而通知也成为了很多应用的必备功能,开发者们都希望通知能带来更好地体验和更多的用户。
但是理想的丰满并不能弥补现实的骨感。自从在 iOS 3 引入 Push Notification 后,之后几乎每个版本 Apple 都在加强这方面的功能。我们可以回顾一下整个历程和相关的主要 API:
iOS 3 - 引入推送通知UIApplication 的 registerForRemoteNotificationTypes 与UIApplicationDelegate 的
application(_:didRegisterForRemoteNotificationsWithDeviceToken:),application(_:didReceiveRemoteNotification:)iOS 4 - 引入本地通知scheduleLocalNotification,presentLocalNotificationNow:,application(_:didReceive:)iOS 5 - 加入通知中心页面iOS 6 - 通知中心页面与 iCloud 同步iOS 7 - 后台静默推送application(_:didReceiveRemoteNotification:fetchCompletionHandle:)iOS 8 - 重新设计 notification 权限请求,Actionable 通知registerUserNotificationSettings(_:),UIUserNotificationAction 与UIUserNotificationCategory,application(_:handleActionWithIdentifier:forRemoteNotification:completionHandler:)
等iOS 9 - Text Input action,基于 HTTP/2 的推送请求UIUserNotificationActionBehavior,全新的 Provider API 等
有点晕,不是么?一个开发者很难在不借助于文档的帮助下区分 application(_:didReceiveRemoteNotification:) 和application(_:didReceiveRemoteNotification:fetchCompletionHandle:),新入行的开发者也不可能明白registerForRemoteNotificationTypes 和
registerUserNotificationSettings(_:) 之间是不是有什么关系,Remote 和 Local Notification 除了在初始化方式之外那些细微的区别也让人抓狂,而很多 API 都被随意地放在了UIApplication 或者
UIApplicationDelegate 中。除此之外,应用已经在前台时,远程推送是无法直接显示的,要先捕获到远程来的通知,然后再发起一个本地通知才能完成现实。更让人郁闷的是,应用在运行时和非运行时捕获通知的路径还不一致。虽然这些种种问题都是由一定历史原因造成的,但不可否认,正是混乱的组织方式和之前版本的考虑不周,使得 iOS 通知方面的开发一直称不上“让人愉悦”,甚至有不少“坏代码”的味道。
另一方面,现在的通知功能相对还是简单,我们能做的只是本地或者远程发起通知,然后显示给用户。虽然 iOS 8 和 9 中添加了按钮和文本来进行交互,但是已发出的通知不能更新,通知的内容也只是在发起时唯一确定,而这些内容也只能是简单的文本。 想要在现有基础上扩展通知的功能,势必会让原本就盘根错节的 API 更加难以理解。
在 iOS 10 中新加入 UserNotifications 框架,可以说是 iOS SDK 发展到现在的最大规模的一次重构。新版本里通知的相关功能被提取到了单独的框架,通知也不再区分类型,而有了更统一的行为。我们接下来就将由浅入深地解析这个重构后的框架的使用方式。
UserNotifications 框架解析
iOS 10 中通知相关的操作遵循下面的流程:
首先你需要向用户请求推送权限,然后发送通知。对于发送出的通知,如果你的应用位于后台或者没有运行的话,系统将通过用户允许的方式 (弹窗,横幅,或者是在通知中心) 进行显示。如果你的应用已经位于前台正在运行,你可以自行决定要不要显示这个通知。最后,如果你希望用户点击通知能有打开应用以外的额外功能的话,你也需要进行处理。
iOS 8 之前,本地推送 (UILocalNotification) 和远程推送 (Remote Notification) 是区分对待的,应用只需要在进行远程推送时获取用户同意。iOS 8 对这一行为进行了规范,因为无论是本地推送还是远程推送,其实在用户看来表现是一致的,都是打断用户的行为。因此从 iOS 8 开始,这两种通知都需要申请权限。iOS 10 里进一步消除了本地通知和推送通知的区别。向用户申请通知权限非常简单:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
granted, error in
if granted {
当然,在使用 UN 开头的 API 的时候,不要忘记导入 UserNotifications 框架:
import UserNotifications
第一次调用这个方法时,会弹出一个系统弹窗。
要注意的是,一旦用户拒绝了这个请求,再次调用该方法也不会再进行弹窗,想要应用有机会接收到通知的话,用户必须自行前往系统的设置中为你的应用打开通知,而这往往是不可能的。因此,在合适的时候弹出请求窗,在请求权限前预先进行说明,而不是直接粗暴地在启动的时候就进行弹窗,会是更明智的选择。
一旦用户同意后,你就可以在应用中发送本地通知了。不过如果你通过服务器发送远程通知的话,还需要多一个获取用户 token 的操作。你的服务器可以使用这个 token 将用向 Apple Push Notification 的服务器提交请求,然后 APNs 通过 token 识别设备和应用,将通知推给用户。
提交 token 请求和获得 token 的回调是现在“唯二”不在新框架中的 API。我们使用 UIApplication 的
registerForRemoteNotifications 来注册远程通知,在 AppDelegate 的
application(_:didRegisterForRemoteNotificationsWithDeviceToken) 中获取用户 token:
UIApplication.shared.registerForRemoteNotifications()
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenString = deviceToken.hexString
print(&Get Push token: \(tokenString)&)
获取得到的 deviceToken 是一个 Data 类型,为了方便使用和传递,我们一般会选择将它转换为一个字符串。Swift 3 中可以使用下面的Data 扩展来构造出适合传递给 Apple 的字符串:
extension Data {
var hexString: String {
return withUnsafeBytes {(bytes: UnsafePointer&UInt8&) -& String in
let buffer = UnsafeBufferPointer(start: bytes, count: count)
return buffer.map {String(format: &%02hhx&, $0)}.reduce(&&, { $0 + $1 })
用户可以在系统设置中修改你的应用的通知权限,除了打开和关闭全部通知权限外,用户也可以限制你的应用只能进行某种形式的通知显示,比如只允许横幅而不允许弹窗及通知中心显示等。一般来说你不应该对用户的选择进行干涉,但是如果你的应用确实需要某种特定场景的推送的话,你可以对当前用户进行的设置进行检查:
UNUserNotificationCenter.current().getNotificationSettings {
settings in
print(settings.authorizationStatus) // .authorized | .denied | .notDetermined
print(settings.badgeSetting) // .enabled | .disabled | .notSupported
关于权限方面的使用,可以参考 Demo 中
UserNotifications 中对通知进行了统一。我们通过通知的内容 (UNNotificationContent),发送的时机 (UNNotificationTrigger) 以及一个发送通知的String 类型的标识符,来生成一个
UNNotificationRequest 类型的发送请求。最后,我们将这个请求添加到UNUserNotificationCenter.current() 中,就可以等待通知到达了:
let content = UNMutableNotificationContent()
content.title = &Time Interval Notification&
content.body = &My first notification&
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let requestIdentifier = &com.onevcat.usernotification.myFirstNotification&
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if error == nil {
print(&Time Interval Notification scheduled: \(requestIdentifier)&)
iOS 10 中通知不仅支持简单的一行文字,你还可以添加 title 和 subtitle,来用粗体字的形式强调通知的目的。对于远程推送,iOS 10 之前一般只含有消息的推送 payload 是这样的:
&alert&:&Test&,
&sound&:&default&,
如果我们想要加入 title 和 subtitle 的话,则需要将 alert 从字符串换为字典,新的 payload 是:
&title&:&I am title&,
&subtitle&:&I am subtitle&,
&body&:&I am body&
&sound&:&default&,
好消息是,后一种字典的方法其实在 iOS 8.2 的时候就已经存在了。虽然当时 title 只是用在 Apple Watch 上的,但是设置好body 的话在 iOS 上还是可以显示的,所以针对 iOS 10 添加标题时是可以保证前向兼容的。
另外,如果要进行本地化对应,在设置这些内容文本时,本地可以使用 String.localizedUserNotificationString(forKey: &your_key&, arguments: []) 的方式来从 Localizable.strings 文件中取出本地化字符串,而远程推送的话,也可以在 payload 的 alert 中使用loc-key 或者
title-loc-key 来进行指定。关于 payload 中的 key,可以参考。
触发器是只对本地通知而言的,远程推送的通知的话默认会在收到后立即显示。现在 UserNotifications 框架中提供了三种触发器,分别是:在一定时间后触发UNTimeIntervalNotificationTrigger,在某月某日某时触发
UNCalendarNotificationTrigger 以及在用户进入或是离开某个区域时触发UNLocationNotificationTrigger。
请求标识符可以用来区分不同的通知请求,在将一个通知请求提交后,通过特定 API 我们能够使用这个标识符来取消或者更新这个通知。我们将在稍后再提到具体用法。
在新版本的通知框架中,Apple 借用了一部分网络请求的概念。我们组织并发送一个通知请求,然后将这个请求提交给 UNUserNotificationCenter 进行处理。我们会在 delegaet 中接收到这个通知请求对应的 response,另外我们也有机会在应用的 extension 中对 request 进行处理。我们在接下来的章节会看到更多这方面的内容。
在提交通知请求后,我们锁屏或者将应用切到后台,并等待设定的时间后,就能看到我们的通知出现在通知中心或者屏幕横幅了:
关于最基础的通知发送,可以参考 Demo 中
取消和更新
在创建通知请求时,我们已经指定了标识符。这个标识符可以用来管理通知。在 iOS 10 之前,我们很难取消掉某一个特定的通知,也不能主动移除或者更新已经展示的通知。想象一下你需要推送用户账户内的余额变化情况,多次的余额增减或者变化很容易让用户十分困惑 - 到底哪条通知才是最正确的?又或者在推送一场比赛的比分时,频繁的通知必然导致用户通知中心数量爆炸,而大部分中途的比分对于用户来说只是噪音。
iOS 10 中,UserNotifications 框架提供了一系列管理通知的 API,你可以做到:
取消还未展示的通知更新还未展示的通知移除已经展示过的通知更新已经展示过的通知
其中关键就在于在创建请求时使用同样的标识符。
比如,从通知中心中移除一个展示过的通知:
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let identifier = &com.onevcat.usernotification.notificationWillBeRemovedAfterDisplayed&
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if error != nil {
print(&Notification request added: \(identifier)&)
delay(4) {
print(&Notification request removed: \(identifier)&)
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
类似地,我们可以使用 removePendingNotificationRequests,来取消还未展示的通知请求。对于更新通知,不论是否已经展示,都和一开始添加请求时一样,再次将请求提交给UNUserNotificationCenter 即可:
UNUserNotificationCenter.current().add(request) { error in
if error != nil {
print(&Notification request added: \(identifier)&)
delay(2) {
let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let newRequest = UNNotificationRequest(identifier: identifier, content:newContent, trigger: newTrigger)
UNUserNotificationCenter.current().add(newRequest) { error in
if error != nil {
print(&Notification request updated: \(identifier)&)
远程推送可以进行通知的更新,在使用 Provider API 向 APNs 提交请求时,在 HTTP/2 的 header 中 apns-collapse-id key 的内容将被作为该推送的标识符进行使用。多次推送同一标识符的通知即可进行更新。
对应本地的 removeDeliveredNotifications,现在还不能通过类似的方式,向 APNs 发送一个包含 collapse id 的 DELETE 请求来删除已经展示的推送,APNs 服务器并不接受一个 DELETE 请求。不过从技术上来说 Apple 方面应该不存在什么问题,我们可以拭目以待。现在如果想要消除一个远程推送,可以选择使用后台静默推送的方式来从本地发起一个删除通知的调用。关于后台推送的部分,可以参考我之前的一篇关于的文章。
关于通知管理,可以参考 Demo 中
的内容。为了能够简单地测试远程推送,一般我们都会用一些方便发送通知的工具, 就是其中之一。我也为 Knuff 添加了apns-collapse-id 的支持,你可以在这个
或者是原 repo 的
中找到相关信息。
应用内展示通知
现在系统可以在应用处于后台或者退出的时候向用户展示通知了。不过,当应用处于前台时,收到的通知是无法进行展示的。如果我们希望在应用内也能显示通知的话,需要额外的工作。
UNUserNotificationCenterDelegate 提供了两个方法,分别对应如何在应用内展示通知,和收到通知响应时要如何处理的工作。我们可以实现这个接口中的对应方法来在应用内展示通知:
class NotificationHandler: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -& Void)
completionHandler([.alert, .sound])
实现后,将 NotificationHandler 的实例赋值给 UNUserNotificationCenter 的delegate 属性就可以了。没有特殊理由的话,AppDelegate 的
application(_:didFinishLaunchingWithOptions:) 就是一个不错的选择:
class AppDelegate: UIResponder, UIApplicationDelegate {
let notificationHandler = NotificationHandler()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -& Bool {
UNUserNotificationCenter.current().delegate = notificationHandler
return true
对通知进行响应
UNUserNotificationCenterDelegate 中还有一个方法,userNotificationCenter(_:didReceive:withCompletionHandler:)。这个代理方法会在用户与你推送的通知进行交互时被调用,包括用户通过通知打开了你的应用,或者点击或者触发了某个 action (我们之后会提到 actionable 的通知)。因为涉及到打开应用的行为,所以实现了这个方法的 delegate 必须在applicationDidFinishLaunching:
返回前就完成设置,这也是我们之前推荐将 NotificationHandler 尽早进行赋值的理由。
一个最简单的实现自然是什么也不错,直接告诉系统你已经完成了所有工作。
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -& Void) {
completionHandler()
想让这个方法变得有趣一点的话,在创建通知的内容时,我们可以在请求中附带一些信息:
let content = UNMutableNotificationContent()
content.title = &Time Interval Notification&
content.body = &My first notification&
content.userInfo = [&name&: &onevcat&]
在该方法里,我们将获取到这个推送请求对应的 response,UNNotificationResponse 是一个几乎包括了通知的所有信息的对象,从中我们可以再次获取到userInfo 中的信息:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -& Void) {
if let name = response.notification.request.content.userInfo[&name&] as? String {
print(&I know it's you! \(name)&)
completionHandler()
更好的消息是,远程推送的 payload 内的内容也会出现在这个 userInfo 中,这样一来,不论是本地推送还是远程推送,处理的路径得到了统一。通过userInfo 的内容来决定页面跳转或者是进行其他操作,都会有很大空间。
Actionable 通知发送和处理
注册 Category
iOS 8 和 9 中 Apple 引入了可以交互的通知,这是通过将一簇 action 放到一个 category 中,将这个 category 进行注册,最后在发送通知时将通知的 category 设置为要使用的 category 来实现的。
注册一个 category 非常容易:
private func registerNotificationCategory() {
let saySomethingCategory: UNNotificationCategory = {
let inputAction = UNTextInputNotificationAction(
identifier: &action.input&,
title: &Input&,
options: [.foreground],
textInputButtonTitle: &Send&,
textInputPlaceholder: &What do you want to say...&)
let goodbyeAction = UNNotificationAction(
identifier: &action.goodbye&,
title: &Goodbye&,
options: [.foreground])
let cancelAction = UNNotificationAction(
identifier: &action.cancel&,
title: &Cancel&,
options: [.destructive])
return UNNotificationCategory(identifier:&saySomethingCategory&, actions: [inputAction, goodbyeAction, cancelAction], intentIdentifiers: [], options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories([saySomethingCategory])
UNTextInputNotificationAction 代表一个输入文本的 action,你可以自定义框的按钮 title 和 placeholder。你稍后会使用identifier 来对 action 进行区分。普通的 UNNotificationAction 对应标准的按钮。为 category 指定一个 identifier,我们将在实际发送通知的时候用这个标识符进行设置,这样系统就知道这个通知对应哪个 category 了。
当然,不要忘了在程序启动时调用这个方法进行注册:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -& Bool {
registerNotificationCategory()
UNUserNotificationCenter.current().delegate = notificationHandler
return true
发送一个带有 action 的通知
在完成 category 注册后,发送一个 actionable 通知就非常简单了,只需要在创建 UNNotificationContent 时把categoryIdentifier 设置为需要的 category id 即可:
content.categoryIdentifier = &saySomethingCategory&
尝试展示这个通知,在下拉或者使用 3D touch 展开通知后,就可以看到对应的 action 了:
远程推送也可以使用 category,只需要在 payload 中添加 category 字段,并指定预先定义的 category id 就可以了:
&alert&:&Please say something&,
&category&:&saySomething&
处理 actionable 通知
和普通的通知并无二致,actionable 通知也会走到 didReceive 的 delegate 方法,我们通过 request 中包含的categoryIdentifier 和 response 里的
actionIdentifier 就可以轻易判定是哪个通知的哪个操作被执行了。对于UNTextInputNotificationAction 触发的 response,直接将它转换为一个
UNTextInputNotificationResponse,就可以拿到其中的用户输入了:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -& Void) {
if let category = UserNotificationCategoryType(rawValue: response.notification.request.content.categoryIdentifier) {
switch category {
case .saySomething:
handleSaySomthing(response: response)
completionHandler()
private func handleSaySomthing(response: UNNotificationResponse) {
let text: String
if let actionType = SaySomethingCategoryAction(rawValue: response.actionIdentifier) {
switch actionType {
case .input: text = (response as! UNTextInputNotificationResponse).userText
case .goodbye: text = &Goodbye&
case .none: text = &&
if !text.isEmpty {
UIAlertController.showConfirmAlertFromTopViewController(message: &You just said \(text)&)
上面的代码先判断通知响应是否属于 &saySomething&,然后从用户输入或者是选择中提取字符串,并且弹出一个 alert 作为响应结果。当然,更多的情况下我们会发送一个网络请求,或者是根据用户操作更新一些 UI 等。
关于 Actionable 的通知,可以参考 Demo 中
Notification Extension
iOS 10 中添加了很多 extension,作为应用与系统整合的入口。与通知相关的 extension 有两个:Service Extension 和 Content Extension。前者可以让我们有机会在收到远程推送的通知后,展示之前对通知内容进行修改;后者可以用来自定义通知视图的样式。
截取并修改通知内容
NotificationService 的模板已经为我们进行了基本的实现:
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -& Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -& Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
if request.identifier == &mutableContent& {
bestAttemptContent.body = &\(bestAttemptContent.body), onevcat&
contentHandler(bestAttemptContent)
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent =
bestAttemptContent {
contentHandler(bestAttemptContent)
didReceive: 方法中有一个等待发送的通知请求,我们通过修改这个请求中的 content 内容,然后在限制的时间内将修改后的内容调用通过contentHandler 返还给系统,就可以显示这个修改过的通知了。在一定时间内没有调用 contentHandler 的话,系统会调用这个方法,来告诉你大限已到。你可以选择什么都不做,这样的话系统将当作什么都没发生,简单地显示原来的通知。可能你其实已经设置好了绝大部分内容,只是有很少一部分没有完成,这时你也可以像例子中这样调用contentHandler 来显示一个变更“中途”的通知。
Service Extension 现在只对远程推送的通知起效,你可以在推送 payload 中增加一个 mutable-content 值为 1 的项来启用内容修改:
&title&:&Greetings&,
&body&:&Long time no see&
&mutable-content&:1
这个 payload 的推送得到的结果,注意 body 后面附上了名字。
使用在本机截取推送并替换内容的方式,可以完成端到端 (end-to-end) 的推送加密。你在服务器推送 payload 中加入加密过的文本,在客户端接到通知后使用预先定义或者获取过的密钥进行解密,然后立即显示。这样一来,即使推送信道被第三方截取,其中所传递的内容也还是安全的。使用这种方式来发送密码或者敏感信息,对于一些金融业务应用和聊天应用来说,应该是必备的特性。
在通知中展示图片/视频
相比于旧版本的通知,iOS 10 中另一个亮眼功能是多媒体的推送。开发者现在可以在通知中嵌入图片或者视频,这极大丰富了推送内容的可读性和趣味性。
为本地通知添加多媒体内容十分简单,只需要通过本地磁盘上的文件 URL 创建一个 UNNotificationAttachment 对象,然后将这个对象放到数组中赋值给 content 的attachments 属性就行了:
let content = UNMutableNotificationContent()
content.title = &Image Notification&
content.body = &Show me an image!&
if let imageURL = Bundle.main.url(forResource: &image&, withExtension: &jpg&),
let attachment = try? UNNotificationAttachment(identifier: &imageAttachment&, url: imageURL, options: nil)
content.attachments = [attachment]
在显示时,横幅或者弹窗将附带设置的图片,使用 3D Touch pop 通知或者下拉通知显示详细内容时,图片也会被放大展示:
除了图片以外,通知还支持音频以及视频。你可以将 MP3 或者 MP4 这样的文件提供给系统来在通知中进行展示和播放。不过,这些文件都有尺寸的限制,比如图片不能超过 5MB,视频不能超过 50MB 等,不过对于一般的能在通知中展示的内容来说,这个尺寸应该是绰绰有余了。关于支持的文件格式和尺寸,可以在中进行确认。在创建UNNotificationAttachment
时,如果遇到了不支持的格式,SDK 也会抛出错误。
通过远程推送的方式,你也可以显示图片等多媒体内容。这要借助于上一节所提到的通过 Notification Service Extension 来修改推送通知内容的技术。一般做法是,我们在推送的 payload 中指定需要加载的图片资源地址,这个地址可以是应用 bundle 内已经存在的资源,也可以是网络的资源。不过因为在创建UNNotificationAttachment 时我们只能使用本地资源,所以如果多媒体还不在本地的话,我们需要先将其下载到本地。在完成UNNotificationAttachment
创建后,我们就可以和本地通知一样,将它设置给 attachments 属性,然后调用contentHandler 了。
简单的示例 payload 如下:
&title&:&Image Notification&,
&body&:&Show me an image from web!&
&mutable-content&:1
&image&: &/assets/images/background-cover.jpg&
mutable-content 表示我们会在接收到通知时对内容进行更改,image 指明了目标图片的地址。
在 NotificationService 里,加入如下代码来下载图片,并将其保存到磁盘缓存中:
private func downloadAndSave(url: URL, handler: @escaping (_ localURL: URL?) -& Void) {
let task = URLSession.shared.dataTask(with: url, completionHandler: {
data, res, error in
var localURL: URL? = nil
if let data = data {
let ext = (url.absoluteString as NSString).pathExtension
let cacheURL = URL(fileURLWithPath: FileManager.default.cachesDirectory)
let url = cacheURL.appendingPathComponent(url.absoluteString.md5).appendingPathExtension(ext)
if let _ = try? data.write(to: url) {
localURL = url
handler(localURL)
task.resume()
然后在 didReceive: 中,接收到这类通知时提取图片地址,下载,并生成 attachment,进行通知展示:
if let imageURLString = bestAttemptContent.userInfo[&image&] as? String,
let URL = URL(string: imageURLString)
downloadAndSave(url: URL) { localURL in
if let localURL = localURL {
let attachment = try UNNotificationAttachment(identifier: &image_downloaded&, url: localURL, options: nil)
bestAttemptContent.attachments = [attachment]
print(error)
contentHandler(bestAttemptContent)
关于在通知中展示图片或者视频,有几点想补充说明:
UNNotificationContent 的 attachments 虽然是一个数组,但是系统只会展示第一个 attachment 对象的内容。不过你依然可以发送多个 attachments,然后在要展示的时候再重新安排它们的顺序,以显示最符合情景的图片或者视频。另外,你也可能会在自定义通知展示 UI 时用到多个 attachment。我们接下来一节中会看到一个相关的例子。在当前 beta (iOS 10 beta 4) 中,serviceExtensionTimeWillExpire 被调用之前,你有 30 秒时间来处理和更改通知内容。对于一般的图片来说,这个时间是足够的。但是如果你推送的是体积较大的视频内容,用户又恰巧处在糟糕的网络环境的话,很有可能无法及时下载完成。如果你想在远程推送来的通知中显示应用 bundle 内的资源的话,要注意 extension 的 bundle 和 app main bundle 并不是一回事儿。你可以选择将图片资源放到 extension bundle 中,也可以选择放在 main bundle 里。总之,你需要保证能够获取到正确的,并且你具有读取权限的 url。关于从 extension 中访问 main bundle,可以参看。系统在创建 attachement 时会根据提供的 url 后缀确定文件类型,如果没有后缀,或者后缀无法不正确的话,你可以在创建时通过 UNNotificationAttachmentOptionsTypeHintKey 来。如果使用的图片和视频文件不在你的 bundle 内部,它们将被移动到系统的负责通知的文件夹下,然后在当通知被移除后删除。如果媒体文件在 bundle 内部,它们将被复制到通知文件夹下。每个应用能使用的媒体文件的文件大小总和是有限制,超过限制后创建 attachment 时将抛出异常。可能的所有错误可以在UNError 中找到。
你可以访问一个已经创建的 attachment 的内容,但是要注意权限问题。可以使用 startAccessingSecurityScopedResource 来暂时获取以创建的 attachment 的访问权限。比如:
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()
关于 Service Extension 和多媒体通知的使用,可以参考 Demo 中
自定义通知视图样式
iOS 10 SDK 新加的另一个 Content Extension 可以用来自定义通知的详细页面的视图。新建一个 Notification Content Extension,Xcode 为我们准备的模板中包含了一个实现了UNNotificationContentExtension 的
UIViewController 子类。这个 extension 中有一个必须实现的方法didReceive(_:),在系统需要显示自定义样式的通知详情视图时,这个方法将被调用,你需要在其中配置你的 UI。而 UI 本身可以通过这个 extension 中的 MainInterface.storyboard 来进行定义。自定义 UI 的通知是和通知 category 绑定的,我们需要在 extension 的 Info.plist 里指定这个通知样式所对应的
category 标识符:
系统在接收到通知后会先查找有没有能够处理这类通知的 content extension,如果存在,那么就交给 extension 来进行处理。另外,在构建 UI 时,我们可以通过 Info.plist 控制通知详细视图的尺寸,以及是否显示原始的通知。关于 Content Extension 中的 Info.plist 的 key,可以在中找到详细信息。
虽然我们可以使用包括按钮在内的各种 UI,但是系统不允许我们对这些 UI 进行交互。点击通知视图 UI 本身会将我们导航到应用中,不过我们可以通过 action 的方式来对自定义 UI 进行更新。UNNotificationContentExtension 为我们提供了一个可选方法didReceive(_:completionHandler:),它会在用户选择了某个 action 时被调用,你有机会在这里更新通知的 UI。如果有 UI 更新,那么在方法的completionHandler
中,开发者可以选择传递 .doNotDismiss 来保持通知继续被显示。如果没有继续显示的必要,可以选择.dismissAndForwardAction 或者
.dismiss,前者将把通知的 action 继续传递给应用的UNUserNotificationCenterDelegate 中的
userNotificationCenter(:didReceive:withCompletionHandler),而后者将直接解散这个通知。
如果你的自定义 UI 包含视频等,你还可以实现 UNNotificationContentExtension 里的 media 开头的一系列属性,它将为你提供一些视频播放的控件和相关方法。
关于 Content Extension 和自定义通知样式,可以参考 Demo 中
iOS 10 SDK 中对通知这块进行了 iOS 系统发布以来最大的一次重构,很多“老朋友”都被标记为了 deprecated:
iOS 10 中被标为弃用的 API
UILocalNotificationUIMutableUserNotificationActionUIMutableUserNotificationCategoryUIUserNotificationActionUIUserNotificationCategoryUIUserNotificationSettingshandleActionWithIdentifier:forLocalNotification:handleActionWithIdentifier:forRemoteNotification:didReceiveLocalNotification:withCompletion:didReceiveRemoteNotification:withCompletion:
等一系列在 UIKit 中的发送和处理通知的类型及方法。
现状以及尽快使用新的 API
相比于 iOS 早期时代的 API,新的 API 展现出了高度的模块化和统一特性,易用性也非常好,是一套更加先进的 API。如果有可能,特别是如果你的应用是重度依赖通知特性的话,直接从 iOS 10 开始可以让你充分使用在新通知体系的各种特性。
虽然原来的 API 都被标为弃用了,但是如果你需要支持 iOS 10 之前的系统的话,你还是需要使用原来的 API。我们可以使用
if #available(iOS 10.0, *) {
的方式来指针对 iOS 10 进行新通知的适配,并让 iOS 10 的用户享受到新通知带来的便利特性,然后在将来版本升级到只支持 iOS 10 以上时再移除掉所有被弃用的代码。对于优化和梳理通知相关代码来说,新 API 对代码设计和组织上带来的好处足以弥补适配上的麻烦,而且它还能为你的应用提供更好的通知特性和体验,何乐不为呢?
本文已收录于以下专栏:
相关文章推荐
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launc...
一:首先在AppDelegate.m中注册本地通知
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNoti...
之前说会单独整理消息通知的内容,但是因为工(就)作(是)的(很)事(懒)没有更新文章,违背了自己的学习的初衷。因为互联网一定要有危机意识,说不定眼一睁,我们就out丢了饭碗。
Block简介(copy一段)
Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block...
1.Base64简介
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。base64编码一般有下面两个用途:
a)所有的二进制文件,都可以因此转化为可打印的文本编码(都变成ASCI...
最近做一个项目,要用到UITableView中cell的多选和全选。 
上网简单一搜索,发现很多小伙伴对于这块是自己自定义cell且通过代码判断,再去改变UI来实现。很多方法的理论基础基本上都是...
上一篇介绍了如何用Notification Service Extension来实现修改收到的消息内容,这一篇介绍使用Notification Content Extension来实现自定义的通知界面...
在实际开发中,发现使用Block有着比delegate和notification更简洁的优势。于是在目前的项目中大量的使用block。
  在我的头文件我是这样声明使用block的。
@interfa...
在IOS7之后,苹果提供了将NSData转化为Base64编码的API:
/* Create an NSData from a Base-64 encoded NSString using the ...
iOS 10 中,开发者现在可以在通知中嵌入图片、音乐或者视频。
为本地通知添加多媒体内容十分简单,只需要通过文件的NSURL创建一个 UNNotificationAttachment 对象,然后将这...
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 ios unnotification 的文章

 

随机推荐