概序:
主要實作iOS10中
UserNotifications
對帶選擇控制的本地通知的使用,隻要點選了當日日的通知或者進入了app,當日的本地通知不再相應功能;使用
3D-Touch
在桌面上來快速啟動app的功能;使用背景多任務功能;
1、本地通知:
iOS10 全新的
UserNotifications
架構将iOS系統的遠端和本地通知做了統一的管理,下面介紹一下本地通知的一些流程及注意點:
1.1 注冊通知中心: 并且實作響應的代理
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { (resulet, error) in
if resulet {
print("register notification success")
}
else {
print("register notification fail error.localizedDescription:\(error?.localizedDescription)")
}
}
return true
}
// app在前台運作時,收到通知會喚起該方法,但是前提是得 實作該方法 及 實作completionHandler
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Swift.Void){
print("categoryIdentifier: \(notification.request.content.categoryIdentifier)")
completionHandler(.alert)
}
// 使用者收到通知點選進入app的時候喚起,
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Swift.Void) {
let catogoryIdentifier = response.notification.request.content.categoryIdentifier
if catogoryIdentifier == "local_notification" {
// 根據事件的 identifier 來響應對應的通知點選事件
if response.actionIdentifier == "sure_action" {
print("response.actionIdentifier: sure_action")
}
}
completionHandler()
}
1.2 設定自定義的通知類型: 使用
UNNotificationCategory
來管理一組通知的自定義事件, 參數說明:
authenticationRequired: 需要解鎖顯示, 黑色文字, 點選會被登入攔截, 解鎖後也不會打開app
destructive: 紅色文字,點選不會進app
foreground: 黑色文字,點選會進app
let lockAction = UNNotificationAction(identifier: "lock_action", title: "點選解鎖", options: .authenticationRequired)
let cancelAction = UNNotificationAction(identifier: "cancel_action", title: "點選消失", options: .destructive)
let sureAction = UNNotificationAction(identifier: "sure_action", title: "點選進入app", options: .foreground)
//設定一組通知類型,通過 local_notification 來辨別
let category = UNNotificationCategory(identifier: "local_notification", actions: [sureAction, lockAction, cancelAction], intentIdentifiers: [], options: .customDismissAction)
//将該類型的通知加入到 通知中心
UNUserNotificationCenter.current().setNotificationCategories([category])
1.3 向通知中心加入通知: 通知觸發器有四種方式:
UNPushNotificationTrigger: 觸發APNS服務,系統自動設定(這是區分本地通知和遠端通知的辨別)
UNTimeIntervalNotificationTrigger: 一段時間後觸發
UNCalendarNotificationTrigger: 指定日期觸發
UNLocationNotificationTrigger: 根據位置觸發,支援進入某地或者離開某地或者都有
具體使用方式對應的api都有詳細的說明。
需要注意的是:
通知間隔設定需要 60S 以上,更換通知聲音時,記得先解除安裝app然後重新運作;
向消息通知注冊多條 通知時,記得 request 的 identifier 不能用一個,如果設定了多條request,但是identifier都是一樣的,隻會觸發最晚的那條通知。
LocalNotifManager.swift
// 在 dateString 之後,每一分鐘執行一次通知
// dateString:觸發通知的時間 sound:通知觸發時聲音 index:第幾次注冊通知 times:共需要注冊幾次
func addNotifi(dateString : String, sound : UNNotificationSound, index : Int, times : Int, week : Int) {
if index >= times {
return
}
let newDateString = LocalNotifManager.getRemindTimeWithString(remindTime: dateString, afterMinter: index)
let array = newDateString.components(separatedBy: ":")
let hour = Int(array[0]);
let minute = Int(array[1]);
var component = DateComponents()
component.hour = hour
component.minute = minute
let trigger = UNCalendarNotificationTrigger.init(dateMatching: component, repeats: true)
// 通知上下文,通過categoryIdentifier來喚起對應的 通知類型
let content = UNMutableNotificationContent()
content.categoryIdentifier = "local_notification"
content.title = "通知标題-_-"
content.body = "通知實體*_*"
content.sound = sound
let request = UNNotificationRequest(identifier: "request"+dateString+String(index), content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
print("week:\(component.weekday) hour:\(component.hour) minute:\(component.minute) index : \(index) success")
})
// 預留一小段時間處理下一條通知的加入
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.addNotifi(dateString: dateString, sound: sound, index: index+1, times: times, week: week)
}
}
class func getRemindTimeWithString(remindTime : String, afterMinter : Int) -> String {
let array = remindTime.components(separatedBy: ":")
if array.count == 2 {
var hour = Int(array[0]);
var minter = Int(array[1]);
var totalMinter = hour! * 60 + minter!
totalMinter += afterMinter
hour = Int(totalMinter / 60)
minter = Int(totalMinter % 60)
if minter! > 9 {
return String(hour!) + ":" + String(minter!)
}
else {
return String(hour!) + ":0" + String(minter!)
}
}
return remindTime;
}
好了,就這樣調用如下代碼即可 在每日的
12:34 - 12:44
時間段中每隔一分鐘執行一次本地通知
LocalNotifManager().setLocalNotification(with: "12:34", times: 10)
1.4 繼續完善需求咯,點選進入app後,不再接受當日的其他通知,這個在網上确實找了好多資料,都沒有找到好的方式來解決,後來隻能說是 天佑殘疾,無意中我在api中發現了兩個重要的方法:
// UNUserNotificationCenter: 擷取還未觸發的通知清單
open func getPendingNotificationRequests(completionHandler: @escaping ([UNNotificationRequest]) -> Swift.Void)
// UNCalendarNotificationTrigger: 擷取該通知下一個觸發的時間日期
open func nextTriggerDate() -> Date?
有了這兩個接口就好辦多了,大概的思路是在app進入前台時,先通過
getPendingNotificationRequests
拿到通知中心的所有未觸發通知,再取消所有的通知,然後在拿到的所有通知清單中,一一找到通知的下一個觸發時間和現在時間對比,如果大于等于一天則重新加入通知中心:
func applicationWillEnterForeground(_ application: UIApplication) {
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (requests : [UNNotificationRequest]) in
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
let formatter = DateFormatter()
formatter.dateFormat = "dd"
let dateString = formatter.string(from: Date())
let dateInt = Int(dateString)
for request in requests {
let trigger = request.trigger as! UNCalendarNotificationTrigger
let nextTriggerDate = trigger.nextTriggerDate()
let nextTriggerDateString = formatter.string(from: nextTriggerDate!)
let nextTriggerDateInt = Int(nextTriggerDateString)
print("dateInt:\(dateInt) nextTriggerDateInt:\(nextTriggerDateInt)")
//觸發時間比現在在 0 天以後,說明是第二天的通知了,需要重新加入到通知中心
if nextTriggerDateInt! - dateInt! > 0 {
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
})
}
}
})
}
2、 3D-Touch
體驗
3D-Touch
這個功能很簡單,這裡隻是做一下記錄吧:
<!-- info.plist添加如下代碼 -->
<key>UIApplicationShortcutItems</key>
<array>
<dict>
<key>UIApplicationShortcutItemTitle</key>
<string>現在穿衣服吧</string>
<key>UIApplicationShortcutItemType</key>
<string>com.harry.HDLocalNotification.wear</string>
<key>UIApplicationShortcutItemIconFile</key>
<string>3d_touch _wear_icon</string>
<key>UIApplicationShortcutItemUserInfo</key>
<dict>
<key>key1</key>
<string>value1</string>
</dict>
</dict>
<dict>
<key>UIApplicationShortcutItemTitle</key>
<string>現在脫衣服吧</string>
<key>UIApplicationShortcutItemType</key>
<string>com.harry.HDLocalNotification.notWear</string>
<key>UIApplicationShortcutItemIconFile</key>
<string>3d_touch_not_wear_icon</string>
<key>UIApplicationShortcutItemUserInfo</key>
<dict>
<key>key2</key>
<string>value2</string>
</dict>
</dict>
</array>
// AppDelegate: 根據info.plist中的 UIApplicationShortcutItemType值 找到對應的事件即可
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
print("shortcutItem.type: \(shortcutItem.type)")
}
3、使用背景多任務功能
這個是在支援多個通知的時候使用,因為多個通知添加到通知中心時,我使用了隊列處理,數量多的時候有一定的耗時,這個時候可能就需要背景多任務來跑這些任務了:
//背景任務
var backgroundTask : UIBackgroundTaskIdentifier! = nil
//進入背景後
func applicationDidEnterBackground(_ application: UIApplication) {
//如果已存在背景任務,先将其設為完成
if self.backgroundTask != nil {
application.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
}
//注冊背景任務
self.backgroundTask = application.beginBackgroundTask(expirationHandler: {
() -> Void in
//如果沒有調用endBackgroundTask,時間耗盡時應用程式将被終止
application.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
})
}
完整項目位址: HDLocalNotification
歡迎 star