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>