iOS10通知使用/3D-Touch使用

iOS10通知使用/3D-Touch使用

概序:

主要实现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 体验

这个功能很简单,这里只是做一下记录吧:

<!-- 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 在iOS10上,苹果将原来散落在UIKit中各处的用户通知相关的代码进行重构,剥离,打造了一个全新的通知框架-Us...
    Nemocdz阅读 2,448评论 3 16
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,472评论 25 707
  • 介绍一下iOS10的通知新功能,用户体验的提升和开发者能够发挥的地方非常多,使得iOS更具有竞争力。 1.iOS ...
    F麦子阅读 3,792评论 3 4
  • 收听提示: 1.点击之后若显示“网页无法打开”,可继续点右上角,选择“在浏览器中打开”,即可。 2.也可下载荔枝F...
    粘粘啊阅读 136评论 2 3
  • 孩子开学后3天时间了! 还是有些不舍,担心儿子的集体生活是否对成长更为重要?我应该做些什么对儿子有影响作用? 看群...
    蔷薇竹阅读 142评论 0 0