在看之前如果你對(duì)iOS10 的推送還處于一片空白屋摔,建議先看<a href="http://www.reibang.com/p/1f3501dd232a">iOS 10 推送你玩過(guò)了嗎薛匪?</a>
Notification Extension
iOS10 添加了很多的Extension,與通知相關(guān)的 extension 有兩個(gè):Service Extension 和 Content Extension四敞。
我們先來(lái)了解一下Content Extension移剪,這個(gè)東西主要是干啥的呢号杏?
可以通過(guò)提前配置的categoryIdentifier來(lái)定制推送顯示的界面誓斥。
簡(jiǎn)單來(lái)說(shuō)只洒,在Content Extension的Info.plist中提前配置categoryIdentifier類型,當(dāng)收到的推送中的categoryIdentifier和Content Extension中提前配置的categoryIdentifier一樣就會(huì)去走自定義的UI展示岖食。
看一下效果圖:
把玩一下:
1红碑、創(chuàng)建
創(chuàng)建完畢相對(duì)于之前項(xiàng)目有啥變化:
2、把玩一下
測(cè)試發(fā)送一個(gè)本地推送
// 測(cè)試按鈕的點(diǎn)擊事件5
func clickBtn5(sender:UIButton) {
if #available(iOS 10.0, *) {
// 1泡垃、創(chuàng)建推送的內(nèi)容
let content = UNMutableNotificationContent()
content.title = "iOS 10 的推送標(biāo)題"
content.body = "附件"
content.subtitle = "附件"
content.userInfo = ["name":"張三","age":"20"]
content.categoryIdentifier = "myNotificationCategory"
// 2析珊、創(chuàng)建發(fā)送觸發(fā)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
// 3. 發(fā)送請(qǐng)求標(biāo)識(shí)符
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、創(chuàng)建一個(gè)發(fā)送請(qǐng)求
let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)
// 5蔑穴、將請(qǐng)求添加到發(fā)送中心
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
if error == nil{
print("Time Interval Notification scheduled: \(requestIdentifier)")
}
})
} else {
// Fallback on earlier versions
}
}
這個(gè)代碼和之前講解的<a href="">本地推送</a>的代碼一樣忠寻,唯一不一樣的就是<code>content.categoryIdentifier = "myNotificationCategory"</code>,
為什么要設(shè)置<code>content.categoryIdentifier = "myNotificationCategory"</code>?
因?yàn)椋?/p>
收到推送之后
當(dāng)下拉通知存和,查看通知詳情的時(shí)候就會(huì)走相應(yīng)的斷點(diǎn)
什么奕剃?你的斷點(diǎn)沒(méi)走?
<1>捐腿、首先檢查你的推送的content.categoryIdentifier = "myNotificationCategory"
<2>纵朋、確定當(dāng)前調(diào)試的tag是Content Extension
<3>、運(yùn)行的時(shí)候確定選擇的app是當(dāng)前正在運(yùn)行的app
放過(guò)斷點(diǎn)茄袖,查看展示的UI操软。
3、定制有顏色的這一部分的UI
還有個(gè)地方說(shuō)一下
Info.plist中的UNNotificationExtensionCategory不僅僅可以定制一個(gè)宪祥,可以定制多個(gè)
變?yōu)?/p>
代碼實(shí)現(xiàn)有顏色部分的布局:
@available(iOSApplicationExtension 10.0, *)
func didReceive(_ notification: UNNotification) {
// 1聂薪、獲取需要顯示的內(nèi)容
let content = notification.request.content
let titleStr = content.title
let subTitleStr = content.subtitle
// 附件的本地url
let finalUrl:URL? = content.attachments[0].url
// 2、通過(guò) category 標(biāo)識(shí)來(lái)判斷應(yīng)該采取哪一種布局
let category = notification.request.content.categoryIdentifier
if category == "myNotificationCategory1" {
// 布局蝗羊,圖片在左邊藏澳,標(biāo)題和子標(biāo)題在右邊
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、加載存儲(chǔ)在沙盒中的附件
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()
}
本地推送的實(shí)現(xiàn)效果
大體的流程實(shí)現(xiàn)是這樣的:
<1>耀找、如果推送消息中的categoryIdentifier和Content Extension的Info.plist中提前配置UNNotificationExtensionCategory中能找到翔悠,那么會(huì)自動(dòng)走Content Extension的類。
<2>、通過(guò)不同的categoryIdentifier進(jìn)行不同的布局凉驻,獲取自己想要展示的通知中的元素
<3>腻要、如果存在多媒體的話,就直接去獲取本地路徑<PS:如果是遠(yuǎn)程推送的話涝登,會(huì)先走Service Extension,即使有通知附件雄家,也會(huì)在這個(gè)時(shí)候下載下來(lái)保存在本地了,所以直接去獲取通知的附件url就可以了>
如果Service Extension還不是很清楚的話可以參考
iOS10 通知extension之 Service Extension你玩過(guò)了嗎胀滚?
4趟济、遠(yuǎn)程推送測(cè)試一下
PS:介紹一個(gè)好用的測(cè)試工具 <a >Kunff</a>
也可以看一下<a href="http://www.reibang.com/p/5d83250fb5a1">iOS10 遠(yuǎn)程推送你玩過(guò)了嗎?</a>更好的了解下iOS10遠(yuǎn)程推送咽笼。
{
"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
}
}
實(shí)現(xiàn)效果:
4顷编、這里說(shuō)一下坑點(diǎn)
<1>、剛開始的時(shí)候我是這么獲取下載圖片路徑的剑刑,因?yàn)檫h(yuǎn)程推送在Service Extension的時(shí)候就將推送的附件下載到本地媳纬,并且保存在沙盒中了,名稱我也知道施掏,這個(gè)時(shí)候我去Content Extension去直接獲取這個(gè)沙盒地址钮惠,去找這個(gè)文件的時(shí)候,發(fā)現(xiàn)找不到七芭。原因是這樣的素挽,因?yàn)槊總€(gè)target中的沙盒地址不一樣。
所以這種方法是不可行的狸驳。
<2>预明、直接獲取通知的附件的url去加載
然后去直接去用這個(gè)路徑去獲取的話還是獲取不到的。
<3>耙箍、當(dāng)需要訪問(wèn)不在 App 自身的沙盒或者自身共享容器里的資源時(shí)撰糠,需要申請(qǐng)權(quán)限訪問(wèn),使用到 NSURL 的兩個(gè)方法:
開始安全訪問(wèn):- (BOOL)startAccessingSecurityScopedResource
停止安全訪問(wèn):- (void)stopAccessingSecurityScopedResource
也就是需要這么訪問(wèn)
// 附件的本地的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()
}
但是這么訪問(wèn)另一個(gè)奇葩的問(wèn)題出來(lái)了辩昆。
本地推送的效果:
遠(yuǎn)程推送的效果:
報(bào)錯(cuò)信息:
ImageIO: createDataWithMappedFile:1322: 'open' failed '/var/mobile/Library/SpringBoard/PushStore/Attachments/com.yanzhang.PushDemo001/c9e9b97a225eeddad295d3a7840101df60d91099.jpeg'
error = 1 (Operation not permitted)
具體的原因沒(méi)找到窗慎,找到一個(gè)類似的原因,說(shuō)在讀取文件的時(shí)候卤材,文件被刪除了,所以為了阻止這種情況的出現(xiàn)峦失,要先把文件轉(zhuǎn)化成data對(duì)象扇丛,然后在讀取的時(shí)候再轉(zhuǎn)成圖片。
所以最終的版本:
// 附件的本地的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()
}
最終實(shí)現(xiàn)愉快的顯示
本地推送尉辑,自動(dòng)布局
遠(yuǎn)程推送自動(dòng)布局
5帆精、擴(kuò)展
<1>、介紹下這個(gè)UNNotificationExtensionDefaultContentHidden配置參數(shù)
這個(gè)參數(shù)什么作用?
直接看圖比較直接卓练,兩圖頂多言隘蝎。
UNNotificationExtensionDefaultContentHidden = YES的時(shí)候
UNNotificationExtensionDefaultContentHidden = NO的時(shí)候
<2>、介紹下這個(gè)UNNotificationExtensionInitialContentSizeRatio配置參數(shù)
這個(gè)參數(shù)什么作用襟企?
這個(gè)值是一定要有的嘱么,系統(tǒng)已經(jīng)默認(rèn)創(chuàng)建好了
這個(gè)值的類型是一個(gè)浮點(diǎn)類型,代表的是高度與寬度的比值顽悼。系統(tǒng)會(huì)使用這個(gè)比值曼振,作為初始化view的大小。舉個(gè)簡(jiǎn)單的例子來(lái)說(shuō)蔚龙,如果該值為1冰评,則該視圖為正方
形。如果為0.5木羹,則代表高度是寬度的一半甲雅。
注意這個(gè)值只是初始化的一個(gè)值,在這個(gè)擴(kuò)展添加后坑填,可以重寫frame抛人,展示的時(shí)候,在我們還沒(méi)打開這個(gè)視圖預(yù)覽時(shí)穷遂,背景是個(gè)類似圖片占位的灰色函匕,那個(gè)灰色的高度寬度
之比,就是通過(guò)這個(gè)值來(lái)設(shè)定蚪黑。
直接上圖
當(dāng)UNNotificationExtensionInitialContentSizeRatio = 1的時(shí)候
<3>盅惜、現(xiàn)在的界面要不就太高,要不就太低忌穿,怎么搞抒寂?來(lái)看一下preferredContentSize這個(gè)屬性。
// 修改整體的高度
preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 150)
這樣就能動(dòng)態(tài)的計(jì)算想要顯示的內(nèi)容的整體的大小了掠剑,但是不好的地方就是屈芜,等內(nèi)容加載出來(lái)之后會(huì)有個(gè)變大或者變小的動(dòng)畫。不過(guò)還能接受朴译。
關(guān)于斷點(diǎn)調(diào)試井佑,參考下面的文章,下面的位置有相關(guān)的說(shuō)明:
http://www.reibang.com/p/5fc3b4155257
最后眠寿,獻(xiàn)上參考Demo地址:https://github.com/RunOfTheSnail/PushDemo001
參考資料:
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
圖畫的不錯(cuò)
http://www.reibang.com/p/2f3202b5e758
http://www.cocoachina.com/ios/20161021/17820.html