iOS10 通知extension之 Content Extension你玩過了嗎?

eyoc9934 7年前發布 | 7K 次閱讀 iOS開發 移動開發

Notification Extension

iOS10 添加了很多的 Extension ,與通知相關的 extension 有兩個: Service ExtensionContent Extension

我們先來了解一下 Content Extension ,這個東西主要是干啥的呢?

可以通過提前配置的 categoryIdentifier 來定制推送顯示的界面。

簡單來說,在 Content ExtensionInfo.plist 中提前配置 categoryIdentifier 類型,當收到的推送中的 categoryIdentifierContent Extension 中提前配置的 categoryIdentifier 一樣就會去走自定義的UI展示。

看一下效果圖:

Paste_Image.png

把玩一下:

1、創建

Paste_Image.png

Paste_Image.png

Paste_Image.png

Paste_Image.png

創建完畢相對于之前項目有啥變化:

Paste_Image.png

Paste_Image.png

Paste_Image.png

2、把玩一下

測試發送一個本地推送

// 測試按鈕的點擊事件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
        }

    }

這個代碼和之前講解的 本地推送 的代碼一樣,唯一不一樣的就是 content.categoryIdentifier = "myNotificationCategory" ,

為什么要設置 content.categoryIdentifier = "myNotificationCategory" ?

因為:

Paste_Image.png

收到推送之后

Paste_Image.png

當下拉通知,查看通知詳情的時候就會走相應的斷點

Paste_Image.png

什么?你的斷點沒走?

<1>、首先檢查你的推送的 content.categoryIdentifier = "myNotificationCategory"

<2>、確定當前調試的tag是 Content Extension

Paste_Image.png

<3>、運行的時候確定選擇的app是當前正在運行的app

Paste_Image.png

放過斷點,查看展示的UI。

Paste_Image.png

Paste_Image.png

3、定制有顏色的這一部分的UI

Paste_Image.png

還有個地方說一下

Info.plist中的 UNNotificationExtensionCategory 不僅僅可以定制一個,可以定制多個

Paste_Image.png

變為

Paste_Image.png

代碼實現有顏色部分的布局:

@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()
    }

本地推送的實現效果

Paste_Image.png

大體的流程實現是這樣的:

<1>、如果推送消息中的 categoryIdentifierContent ExtensionInfo.plist 中提前配置 UNNotificationExtensionCategory 中能找到,那么會自動走 Content Extension 的類。

<2>、通過不同的 categoryIdentifier 進行不同的布局,獲取自己想要展示的通知中的元素

<3>、如果存在多媒體的話,就直接去獲取本地路徑<PS:如果是遠程推送的話,會先走**Service Extension**,即使有通知附件,也會在這個時候下載下來保存在本地了,所以直接去獲取通知的附件url就可以了>

4、遠程推送測試一下

Paste_Image.png

{
  "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
  }
}

實現效果:

Paste_Image.png

5、這里說一下坑點

<1>、剛開始的時候我是這么獲取下載圖片路徑的,因為遠程推送在 Service Extension 的時候就將推送的附件下載到本地,并且保存在沙盒中了,名稱我也知道,這個時候我去 Content Extension 去直接獲取這個沙盒地址,去找這個文件的時候,發現找不到。原因是這樣的,因為每個target中的沙盒地址不一樣。

Paste_Image.png

Paste_Image.png

所以這種方法是不可行的。

<2>、直接獲取通知的附件的url去加載

Paste_Image.png

然后去直接去用這個路徑去獲取的話還是獲取不到的。

<3>、當需要訪問不在 App 自身的沙盒或者自身共享容器里的資源時,需要申請權限訪問,使用到 NSURL 的兩個方法:

開始安全訪問: - (BOOL)startAccessingSecurityScopedResource

停止安全訪問: - (void)stopAccessingSecurityScopedResource

也就是需要這么訪問

// 附件的本地的url

let finalUrl:URL? = content.attachments[0].url

if (finalUrl?.startAccessingSecurityScopedResource())!{

contentImageView.image = UIImage(contentsOfFile: finalUrl!.path)

subImageView.image = UIImage(contentsOfFile: finalUrl!.path)

subImageView.contentMode = UIViewContentMode.scaleAspectFit

finalUrl?.stopAccessingSecurityScopedResource()

}

但是這么訪問另一個奇葩的問題出來了。

本地推送的效果:

yuan'ch

遠程推送的效果:

Paste_Image.png

報錯信息:

ImageIO: createDataWithMappedFile:1322:  'open' failed '/var/mobile/Library/SpringBoard/PushStore/Attachments/com.yanzhang.PushDemo001/c9e9b97a225eeddad295d3a7840101df60d91099.jpeg'
         error = 1 (Operation not permitted)

具體的原因沒找到,找到一個類似的原因,說在讀取文件的時候,文件被刪除了,所以為了阻止這種情況的出現,要先把文件轉化成data對象,然后在讀取的時候再轉成圖片。

所以最終的版本:

// 附件的本地的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()
        }

最終實現愉快的顯示

本地推送,自動布局

Paste_Image.png

遠程推送自動布局

Paste_Image.png

6、擴展

<1>、介紹下這個 UNNotificationExtensionDefaultContentHidden 配置參數

Paste_Image.png

這個參數什么作用?

直接看圖比較直接,兩圖頂多言。

UNNotificationExtensionDefaultContentHidden = YES的時候

Paste_Image.png

UNNotificationExtensionDefaultContentHidden = NO的時候

Paste_Image.png

<2>、介紹下這個 UNNotificationExtensionInitialContentSizeRatio 配置參數

Paste_Image.png

這個參數什么作用?

這個值是一定要有的,系統已經默認創建好了

這個值的類型是一個浮點類型,代表的是高度與寬度的比值。系統會使用這個比值,作為初始化view的大小。舉個簡單的例子來說,如果該值為1,則該視圖為正方

形。如果為0.5,則代表高度是寬度的一半。

注意這個值只是初始化的一個值,在這個擴展添加后,可以重寫frame,展示的時候,在我們還沒打開這個視圖預覽時,背景是個類似圖片占位的灰色,那個灰色的高度寬度

之比,就是通過這個值來設定。

直接上圖

Paste_Image.png

Paste_Image.png

UNNotificationExtensionInitialContentSizeRatio = 1 的時候

Paste_Image.png

<3>、現在的界面要不就太高,要不就太低,怎么搞?來看一下 preferredContentSize 這個屬性。

// 修改整體的高度
preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 150)

這樣就能動態的計算想要顯示的內容的整體的大小了,但是不好的地方就是,等內容加載出來之后會有個變大或者變小的動畫。不過還能接受。

Paste_Image.png

Paste_Image.png

 

參考資料:

http://www.cocoachina.com/ios/20160628/16833.html

https://onevcat.com/2016/08/notification/

http://www.cnblogs.com/lidongq/p/5968923.html

https://developer.apple.com/reference/usernotifications/unnotificationattachment

 

來自:http://www.jianshu.com/p/00f671d204d4

 

 本文由用戶 eyoc9934 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!