iOS10 通知extension之 Content Extension你玩过了吗?
eyoc9934
8年前
<p><strong>Notification Extension</strong></p> <p>iOS10 添加了很多的 <strong>Extension</strong> ,与通知相关的 <strong>extension</strong> 有两个: <strong>Service Extension</strong> 和 <strong>Content Extension</strong> 。</p> <p>我们先来了解一下 <strong>Content Extension</strong> ,这个东西主要是干啥的呢?</p> <p>可以通过提前配置的 <strong>categoryIdentifier</strong> 来定制推送显示的界面。</p> <p>简单来说,在 <strong>Content Extension</strong> 的 <strong>Info.plist</strong> 中提前配置 <strong>categoryIdentifier</strong> 类型,当收到的推送中的 <strong>categoryIdentifier</strong> 和 <strong>Content Extension</strong> 中提前配置的 <strong>categoryIdentifier</strong> 一样就会去走自定义的UI展示。</p> <p>看一下效果图:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4ee717d390012729ad8c4943396bc866.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>把玩一下:</p> <p><strong>1、创建</strong></p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c426c84986b049117faee306232dff86.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4e42886062b1501ee40e0b6b2db5b01c.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/847c8274a7149af067e4e60f6f35e2d4.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/966ae6a199f9e9edf6c508c64d0d829b.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>创建完毕相对于之前项目有啥变化:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fa91b78bf5b6f31d011dffdc5bf345c5.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6b36264d671cd4ac7d1a17381f2158c3.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/0dd2ceb49e6c620947b32749145564f8.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><strong>2、把玩一下</strong></p> <p>测试发送一个本地推送</p> <pre> <code class="language-objectivec">// 测试按钮的点击事件5 func clickBtn5(sender:UIButton) { if #available(iOS 10.0, *) { // 1、创建推送的内容 let content = UNMutableNotificationContent() content.title = "iOS 10 的推送标题" content.body = "附件" content.subtitle = "附件" content.userInfo = ["name":"张三","age":"20"] content.categoryIdentifier = "myNotificationCategory" // 2、创建发送触发 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false) // 3. 发送请求标识符 let requestIdentifier = "music" // 添加图片 if let imageURL = Bundle.main.url(forResource: "二哈", withExtension: "jpg"), let attachment = try? UNNotificationAttachment(identifier: "imageAttachment", url: imageURL, options: nil) { content.attachments = [attachment] } // 添加视频 // if let videoURL = Bundle.main.url(forResource: "IMG_2077", withExtension: "MOV"), // let attachment = try? UNNotificationAttachment(identifier: "videoAttachment", url: videoURL, options: nil) // { // content.attachments = [attachment] // } // // 添加音频 // if let videoURL = Bundle.main.url(forResource: "聂芦苇+-+东京热", withExtension: "mp3"), // let attachment = try? UNNotificationAttachment(identifier: "voiceAttachment", url: videoURL, options: nil) // { // content.attachments = [attachment] // } // 4、创建一个发送请求 let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger) // 5、将请求添加到发送中心 UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in if error == nil{ print("Time Interval Notification scheduled: \(requestIdentifier)") } }) } else { // Fallback on earlier versions } }</code></pre> <p>这个代码和之前讲解的 本地推送 的代码一样,唯一不一样的就是 content.categoryIdentifier = "myNotificationCategory" ,</p> <p>为什么要设置 content.categoryIdentifier = "myNotificationCategory" ?</p> <p>因为:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5d4d99cc338f064de7f338fb7e514ee7.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>收到推送之后</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6c2d974302bd6a9d78e7cc200eeae747.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>当下拉通知,查看通知详情的时候就会走相应的断点</p> <p><img src="https://simg.open-open.com/show/dab28fe44be2b5bd848312cf0e7b5668.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>什么?你的断点没走?</p> <p><1>、首先检查你的推送的 content.categoryIdentifier = "myNotificationCategory"</p> <p><2>、确定当前调试的tag是 <strong>Content Extension</strong></p> <p style="text-align:center"><img src="https://simg.open-open.com/show/17bf1198475ee25c515edce9a3d23124.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><3>、运行的时候确定选择的app是当前正在运行的app</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/3530bc015c9fb969106603c8ebd15446.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>放过断点,查看展示的UI。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ab5574485dfa1cc6defddcdfabf5b28f.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1f05721b680b661a4edbf1c156386c89.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><strong>3、定制有颜色的这一部分的UI</strong></p> <p style="text-align:center"><img src="https://simg.open-open.com/show/cff07b37ef1022fa538003e09f4eab82.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>还有个地方说一下</p> <p>Info.plist中的 <strong>UNNotificationExtensionCategory</strong> 不仅仅可以定制一个,可以定制多个</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4328a325a7b27e8ddf229d2472593667.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>变为</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a0f0ba2c6c18cc5ffaf8bc8cce72852e.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>代码实现有颜色部分的布局:</p> <pre> <code class="language-objectivec">@available(iOSApplicationExtension 10.0, *) func didReceive(_ notification: UNNotification) { // 1、获取需要显示的内容 let content = notification.request.content let titleStr = content.title let subTitleStr = content.subtitle // 附件的本地url let finalUrl:URL? = content.attachments[0].url // 2、通过 category 标识来判断应该采取哪一种布局 let category = notification.request.content.categoryIdentifier if category == "myNotificationCategory1" { // 布局,图片在左边,标题和子标题在右边 contentImageView.frame = CGRect(x: 10, y: 10, width:self.view.frame.width*(1/3.0), height: self.view.frame.width*(1/3.0)) contentImageView.backgroundColor = UIColor.cyan contentImageView.contentMode = UIViewContentMode.scaleAspectFit let titleLabelX = self.contentImageView.frame.maxX+10 self.titleLabel.frame = CGRect(x: titleLabelX, y: 10, width: self.view.frame.width-titleLabelX-10, height: 0) } // 3、加载存储在沙盒中的附件 if (finalUrl?.startAccessingSecurityScopedResource())!{ print("finalUrl = \(finalUrl) finalUrl.path = \(finalUrl?.path)") let tempImage = UIImage(contentsOfFile: finalUrl!.path) let imageDate = UIImageJPEGRepresentation(tempImage!, 1.0) let operateImage = UIImage.init(data: imageDate!) contentImageView.image = operateImage subImageView.image = operateImage //UIImage(contentsOfFile: finalUrl!.path) subImageView.contentMode = UIViewContentMode.scaleAspectFit finalUrl?.stopAccessingSecurityScopedResource() } // 更新titleLabel的值 self.titleLabel.text = titleStr self.titleLabel.sizeToFit() // 重新布局 subTitleLabel self.subTitleLabel.frame = CGRect(x: self.titleLabel.frame.minX, y: self.titleLabel.frame.maxY+5, width:self.view.frame.width*(1/3.0), height: 0) self.subTitleLabel.text = subTitleStr self.subTitleLabel.sizeToFit() }</code></pre> <p>本地推送的实现效果</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a5ed97f1c49ff51805a6220f525d4566.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>大体的流程实现是这样的:</p> <p><1>、如果推送消息中的 <strong>categoryIdentifier</strong> 和 <strong>Content Extension</strong> 的 <strong>Info.plist</strong> 中提前配置 <strong>UNNotificationExtensionCategory</strong> 中能找到,那么会自动走 <strong>Content Extension</strong> 的类。</p> <p><2>、通过不同的 <strong>categoryIdentifier</strong> 进行不同的布局,获取自己想要展示的通知中的元素</p> <p><3>、如果存在多媒体的话,就直接去获取本地路径<PS:如果是远程推送的话,会先走**Service Extension**,即使有通知附件,也会在这个时候下载下来保存在本地了,所以直接去获取通知的附件url就可以了></p> <p><strong>4、远程推送测试一下</strong></p> <p style="text-align:center"><img src="https://simg.open-open.com/show/013bc85d9c93830b2451bddaeb047060.png"></p> <p style="text-align:center">Paste_Image.png</p> <pre> <code class="language-objectivec">{ "aps":{ "alert":{ "title":"iOS 10 title", "subtitle":"iOS 10 subtitle", "body":"iOS 10 body" }, "my-attachment":"http://img01.taopic.com/160317/240440-16031FU23937.jpg", "mutable-content":1, "category":"myNotificationCategory1", "sound":"default", "badge":3 } }</code></pre> <p>实现效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8f0b101b1c463315af48e0708aee0252.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><strong>5、这里说一下坑点</strong></p> <p><1>、刚开始的时候我是这么获取下载图片路径的,因为远程推送在 <strong>Service Extension</strong> 的时候就将推送的附件下载到本地,并且保存在沙盒中了,名称我也知道,这个时候我去 <strong>Content Extension</strong> 去直接获取这个沙盒地址,去找这个文件的时候,发现找不到。原因是这样的,因为每个target中的沙盒地址不一样。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/febc5051c46fc3bd62f109ad2ac5ba75.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/171eb2681faabb2bceaf3e39c0afeefb.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>所以这种方法是不可行的。</p> <p><2>、直接获取通知的附件的url去加载</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/e0d5dae28961f5738b634129cfec96d2.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>然后去直接去用这个路径去获取的话还是获取不到的。</p> <p><3>、当需要访问不在 App 自身的沙盒或者自身共享容器里的资源时,需要申请权限访问,使用到 NSURL 的两个方法:</p> <p>开始安全访问: <a href="/misc/goto?guid=4959741630795268552" rel="nofollow,noindex">- (BOOL)startAccessingSecurityScopedResource</a></p> <p>停止安全访问: <a href="/misc/goto?guid=4959741630885752215" rel="nofollow,noindex">- (void)stopAccessingSecurityScopedResource</a></p> <p>也就是需要这么访问</p> <p>// 附件的本地的url</p> <p>let finalUrl:URL? = content.attachments[0].url</p> <p>if (finalUrl?.startAccessingSecurityScopedResource())!{</p> <p>contentImageView.image = UIImage(contentsOfFile: finalUrl!.path)</p> <p>subImageView.image = UIImage(contentsOfFile: finalUrl!.path)</p> <p>subImageView.contentMode = UIViewContentMode.scaleAspectFit</p> <p>finalUrl?.stopAccessingSecurityScopedResource()</p> <p>}</p> <p>但是这么访问另一个奇葩的问题出来了。</p> <p>本地推送的效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1b142c9cfe7fc871e8c57b84d2ce2e9e.png"></p> <p style="text-align:center">yuan'ch</p> <p>远程推送的效果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/7114feabc22bf620113e4d082eac1379.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>报错信息:</p> <pre> <code class="language-objectivec">ImageIO: createDataWithMappedFile:1322: 'open' failed '/var/mobile/Library/SpringBoard/PushStore/Attachments/com.yanzhang.PushDemo001/c9e9b97a225eeddad295d3a7840101df60d91099.jpeg' error = 1 (Operation not permitted)</code></pre> <p>具体的原因没找到,找到一个类似的原因,说在读取文件的时候,文件被删除了,所以为了阻止这种情况的出现,要先把文件转化成data对象,然后在读取的时候再转成图片。</p> <p>所以最终的版本:</p> <pre> <code class="language-objectivec">// 附件的本地的url let finalUrl:URL? = content.attachments[0].url if (finalUrl?.startAccessingSecurityScopedResource())!{ let tempImage = UIImage(contentsOfFile: finalUrl!.path) let imageDate = UIImageJPEGRepresentation(tempImage!, 1.0) let operateImage = UIImage.init(data: imageDate!) contentImageView.image = operateImage subImageView.image = operateImage //UIImage(contentsOfFile: finalUrl!.path) subImageView.contentMode = UIViewContentMode.scaleAspectFit finalUrl?.stopAccessingSecurityScopedResource() }</code></pre> <p>最终实现愉快的显示</p> <p>本地推送,自动布局</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/3428935e2070765a39da1cfe34461b5c.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>远程推送自动布局</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/63e24ad9acfe5051061b3c719bdaa9a2.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><strong>6、扩展</strong></p> <p><1>、介绍下这个 <strong>UNNotificationExtensionDefaultContentHidden</strong> 配置参数</p> <p><img src="https://simg.open-open.com/show/adedeb3c1927af5af2fa3bd1d6a9b5fd.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>这个参数什么作用?</p> <p>直接看图比较直接,两图顶多言。</p> <p>UNNotificationExtensionDefaultContentHidden = YES的时候</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/97e71af73215f0fe19557b615b0f2be3.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>UNNotificationExtensionDefaultContentHidden = NO的时候</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/2900bbd0eb0fa4396b0793a01398f2c2.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><2>、介绍下这个 <strong>UNNotificationExtensionInitialContentSizeRatio</strong> 配置参数</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/2a9e8e3262473a87a9c07f4b673bdce5.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>这个参数什么作用?</p> <p>这个值是一定要有的,系统已经默认创建好了</p> <p>这个值的类型是一个浮点类型,代表的是高度与宽度的比值。系统会使用这个比值,作为初始化view的大小。举个简单的例子来说,如果该值为1,则该视图为正方</p> <p>形。如果为0.5,则代表高度是宽度的一半。</p> <p>注意这个值只是初始化的一个值,在这个扩展添加后,可以重写frame,展示的时候,在我们还没打开这个视图预览时,背景是个类似图片占位的灰色,那个灰色的高度宽度</p> <p>之比,就是通过这个值来设定。</p> <p>直接上图</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a2df7d2f9d2084e884b508e6b139879c.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/844e7e0d62870d6a51a5773e2fa02cb8.png"></p> <p style="text-align:center">Paste_Image.png</p> <p>当 <strong>UNNotificationExtensionInitialContentSizeRatio = 1</strong> 的时候</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5308730e85aac2ceb552ead59e745d68.png"></p> <p style="text-align:center">Paste_Image.png</p> <p><3>、现在的界面要不就太高,要不就太低,怎么搞?来看一下 <strong>preferredContentSize</strong> 这个属性。</p> <pre> <code class="language-objectivec">// 修改整体的高度 preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 150)</code></pre> <p>这样就能动态的计算想要显示的内容的整体的大小了,但是不好的地方就是,等内容加载出来之后会有个变大或者变小的动画。不过还能接受。</p> <p><img src="https://simg.open-open.com/show/4bc287394a843d3c20efcb581b5216d5.png"></p> <p style="text-align:center">Paste_Image.png</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/d8cf8bfca36a2199cee871a772e10b19.png"></p> <p style="text-align:center">Paste_Image.png</p> <p> </p> <p>参考资料:</p> <p><a href="/misc/goto?guid=4959741630971251727" rel="nofollow,noindex">http://www.cocoachina.com/ios/20160628/16833.html</a></p> <p><a href="/misc/goto?guid=4959741631058702061" rel="nofollow,noindex">https://onevcat.com/2016/08/notification/</a></p> <p><a href="/misc/goto?guid=4959741631132435621" rel="nofollow,noindex">http://www.cnblogs.com/lidongq/p/5968923.html</a></p> <p><a href="/misc/goto?guid=4959741631211296496" rel="nofollow,noindex">https://developer.apple.com/reference/usernotifications/unnotificationattachment</a></p> <p> </p> <p>来自:http://www.jianshu.com/p/00f671d204d4</p> <p> </p>