来搞一搞UserNotifications本地通知😂

UserNotifications
UserNotificationsUI
Human Interface Guidelines - Notifications
Local and Remote Notification Programming Guide
Introduction to Notifications
Advanced Notifications
What's New in the Apple Push Notification Service
Rich Notifications
Best Practices and What’s New in User Notifications

从iOS 10开始,与通知有关的API发生了不小的变化,在使用时,除了需要导入独立的UserNotifications框架之外,还需要获取用户授权:

import UIKit
import UserNotifications

class ViewController: UIViewController {
    
    /// 用于标记通知权限(默认为false)
    var isGrantedNotificationAccess = false

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 获取通知权限
        UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in
            
            // 当用户点击“Allow”时,granted的值为true,
            // 当用户点击“Don't Allow”时,granted的值为false
            self.isGrantedNotificationAccess = granted
            
            // 如果没有获取到用户授权,就会执行下面的代码
            if !granted {
                
                // 可以考虑在这里执行一个弹窗,提示用户获取通知权限
                print("需要获取通知权限才能发送通知.")
            }
        }
    }
}

不管你是发送本地通知,还是远程通知,它本身就是一种中断用户的行为,因此,将是否需要接收通知的权限交还给用户,可以最大限度的提升用户体验:


获取用户授权

一、通知的基本使用

点击“Allow”,获取发送通知的权限,以便后续顺利进行相应的演示。来到Main.storyboard文件中,布局几个按钮,然后给它们拖线。然后回到ViewController文件中,在拖线代码中实现相应的功能:

// MARK: - 发送通知
@IBAction func sendNotifications(_ sender: UIButton) {
    
    // 如果获取到发送通知的权限
    if isGrantedNotificationAccess {
        
        // 创建通知的内容
        let content = UNMutableNotificationContent()
        
        // 设置通知默认提示音
        content.sound = UNNotificationSound.default()
        
        // 设置通知的标题
        content.title = "紧急通知"
        
        // 设置通知的内容
        content.body = "起床啦!你老婆跟人跑了!😳😳😳😳😳"
        
        /** 创建通知触发器 */
        let dateComponents: Set<Calendar.Component> = [.second, .minute, .hour]
        var date = Calendar.current.dateComponents(dateComponents, from: Date())
        date.second = date.second! + 3
        let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: false)
        
        // 创建发送通知请求的标识符
        let identifier = "message.yourWifeRanAway"
        
        // 创建发送通知其的请求,并且将其添加到通知中心
        addNotification(identifier, content, trigger)
    }
}

/// 用于创建发送通知的请求, 并将其添加到通知中心
func addNotification(_ identifier: String, _ content: UNMutableNotificationContent, _ trigger: UNNotificationTrigger?) {
    
    // 创建通知发送请求
    let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
    
    // 将通知发送请求添加到发送中心
    UNUserNotificationCenter.current().add(request) { (error) in
        if error != nil {
            print("error adding notification: \(String(describing: error?.localizedDescription))")
        }
    }
}

在获取到通知权限之后,就可以创建通知了。创建通知的步骤一般为:①、创建通知的内容并设置相应的属性(content);②、创建通知的触发器(trigger);③、创建发送通知请求的标识符(identifier)。完成上面三个步骤以后,就可以创建通知发送请求并将其添加到通知中心了。运行程序,点击发送通知按钮并将程序退到后台:

后台通知演示.jpg

如果你喜欢的话,还可以把创建通知内容的代码封装到一个方法中。另外,如果觉得上面创建通知触发器的代码过于冗余,还可以使用UNTimeIntervalNotificationTrigger(timeInterval: , repeats: )函数:

// MARK: - 又发通知
@IBAction func otherNotifications(_ sender: UIButton) {
    
    // 如果获取到发送通知授权
    if isGrantedNotificationAccess {
        
        // 调用createNotificationContent
        let content = createNotificationContent()
        
        // 创建通知触发器
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5.0, repeats: false)
        
        // 创建发送通知请求的标识符
        let identifier = "message.yourWifeRanAgain" 
        
        // 创建发送通知其的请求,并且将其添加到通知中心
        addNotification(identifier, content, trigger)
    }
}

/// 创建通知的内容
func createNotificationContent() -> UNMutableNotificationContent {
    
    // 创建通知内容
    let content = UNMutableNotificationContent()
    
    // 在App图标上显示通知数量
    content.badge = 5
    
    // 设置通知默认提示音
    content.sound = UNNotificationSound.default()
    
    // 设置通知标题
    content.title = "再次通知"
    
    // 设置通知内容
    content.body = "再不起床,你老婆真跟人跑了!😂😂😂😂😂"
    
    // 设置本地通知的userInfo
    content.userInfo = ["name": "Enrica"]
    
    return content
}

和上面发送通知的代码相比,这里在创建通知内容时,额外的设置了两个属性,一个是badge,另外一个是userInfor。运行程序,效果和上面是一样的:

本地通知的演示.jpg

除了在后台会接收到通知之外,锁屏状态下也是可以收到通知的。点击“又发通知”按钮之后,快速按钮command + L键进入锁屏状态,依然可以收到通知,并且点击通知可以快速预览:

锁屏状态下接收通知.jpg

二、应用内(前台)通知

上面演示的,都是程序退到后台的情况,接下来,我们来演示一下,应用程序在前台时接收通知的情况。来到viewDidLoad方法中,设置通知中心的代理,然后遵守UNUserNotificationCenterDelegate协议,实现协议中的相关方法:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 获取通知权限
    UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in
        
        // 当用户点击“Allow”时,granted的值为true,
        // 当用户点击“Don't Allow”时,granted的值为false
        self.isGrantedNotificationAccess = granted
        
        // 如果没有获取到用户授权,就会执行下面的代码
        if !granted {
            
            // 可以考虑在这里执行一个弹窗,提示用户获取通知权限
            print("需要获取通知权限才能发送通知.")
        }
    }
    
    // 设置UNUserNotificationCenter的代理
    UNUserNotificationCenter.current().delegate = self
}

// MARK: - UNUserNotificationCenterDelegate
extension ViewController: UNUserNotificationCenterDelegate {
    
    // 实现代理方法
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        
        // UNNotificationPresentationOptions的值有3个,但是前台通知不需要badge
        completionHandler([.alert, .sound])
    }
}

因为是在前台接收通知,所以不需要badge,只需要设置alert和sound就可以了。运行程序,点击“发送通知”按钮,然后等一会儿就能看到通知了:

应用内通知.jpg

接下来,我们要看一下通知中心中已存在但是还未经展示的通知请求,以及已经展示过的通知请求。分别实现viewPendingNotifications(_ sender: )方法和viewDeliveredNotifications(_ sender: )方法:

// MARK: - 查看等待通知
@IBAction func viewPendingNotifications(_ sender: UIButton) {
    
    // 从通知中心获取所有还未展示的通知请求
    UNUserNotificationCenter.current().getPendingNotificationRequests { (requestList) in
        
        // 查看当前还有多少个通知未展示
        print("\(Date()) -- 还有\(requestList.count)个通知请求未经展示。")
        
        // 遍历通知请求列表
        for request in requestList {
            print("未展示通知请求的标识符为: \(request.identifier), 内容为: \(request.content.body)")
        }
    }
}

// MARK: - 查看已发送通知
@IBAction func viewDeliveredNotifications(_ sender: UIButton) {
    
    // 从通知中心获取已经展示过的通知
    UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
        
        // 查看当前有多少个通知已经展示过了
        print("\(Date()) -- 已经展示了\(notifications.count)个通知。")
        
        // 遍历已经展示过的通知
        for notification in notifications {
            print("已展示通知请求的标识符为: \(notification.request.identifier), 内容为: \(notification.request.content.body)")
        }
    }
}

getPendingNotificationRequests(completionHandler: @escaping ([UNNotificationRequest]) -> Swift.Void)函数可以获取通知中心里面所有等待触发的通知,而getDeliveredNotifications(completionHandler: @escaping ([UNNotification]) -> Swift.Void)函数可以获取通知中心里面所有已经展示完成的通知。程序运行以后,快速点击“发送通知”按钮和“又发通知”按钮,在通知展示出来之前,迅速点击“查看等待通知按钮”。等所有的通知都展示完成之后,再点击“查看已发送通知”按钮,控制台会打印如下信息:

查看未发送或者已发送的通知.png

三、更新通知

如果我们重复点击“发送通知”按钮,或者“又发通知”按钮,在通知被展示出来之前,点击“查看等待通知”按钮时,你会发现此时只有一条未经展示的通知。原因是,我们通知的内容并没有发生更改,所以不管你点击多少次,它永远都只是一条通知。为了实现每点击一次,重新发送一条通知,我们需要在点击的过程中修改通知的内容:

/// 用于更改通知的内容
fileprivate var index = 0

// MARK: - 又发通知
@IBAction func otherNotifications(_ sender: UIButton) {
    
    // 如果获取到发送通知授权
    if isGrantedNotificationAccess {
        
        // 调用createNotificationContent
        let content = createNotificationContent()
        
        // 修改content的subtitle
        index += 1
        content.subtitle = "你老婆跑了\(index)次!🙄🙄🙄🙄🙄"
        
        // 创建通知触发器
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5.0, repeats: false)
        
        // 创建发送通知请求的标识符
        let identifier = "message.yourWifeRanAgain.\(index)"  // 因为更改了通知的内容,所以标识符也要修改
        
        // 创建发送通知其的请求,并且将其添加到通知中心
        addNotification(identifier, content, trigger)
    }
}

运行程序,连续点击“又发通知”按钮,然后快速点击“查看等待通知”按钮,你会在控制台看到n条等待的通知,并且最后展示的时候,你点了多少下,系统就会推送多少条通知:

连续发送多条通知.jpg

在上面的演示代码中,我们只是通过简单修改通知的标识符来实现连续推送多条通知,接下来,我们要演示一下,通过点击“更新即将显示的通知”按钮来改变处于等待状态(也就是在通知中心里面创建了发送通知请求,但是还未展示出来)通知的内容:

/// 用于更新即将显示通知的内容(body)
    let catchUp = ["还有这种事?", "不能让她跑了!赶紧起床追", "抱着衣服追...☺️☺️☺️", "边追边穿衣服...😱😱😱😱", "还好来得及,赶紧追!"]


// MARK: - 更新即将显示的通知
@IBAction func renewNextNotification(_ sender: UIButton) {
    
    // 从通知中心获取所有未经展示的通知
    UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
        
        // 取出通知中心里面未经展示通知列表中的第一个
        if let request = requests.first {
            
            // 如果该通知请求的标识符前缀为message.yourWifeRanAgain
            if request.identifier.hasPrefix("message.yourWifeRanAgain") {
                
                // 那么就修改通知的内容
                self.updateNotification(request)
                
            } else {
                
                // 如果该通知请求的标识符前缀不是message.yourWifeRanAgain,则执行拷贝
                let content = request.content.mutableCopy() as! UNMutableNotificationContent
                
                // 创建通知请求,并且将其添加到通知中心
                self.addNotification(request.identifier, content, request.trigger)
            }
        }
    }
}

/// 更新即将显示通知的内容
func updateNotification(_ request: UNNotificationRequest) {
    
    // 获取所有标识符前缀为"message.yourWifeRanAgain"的请求
    if request.identifier.hasPrefix("message.yourWifeRanAgain") {
        
        // 根据userInfo中的键取出对应的值,并且将其强转成真实的类型
        var stepNumber = request.content.userInfo["index"] as! Int
        
        // 取余运算,能保证stepNumber的值永远都在有效的范围之内,可以防止数组越界
        stepNumber = (stepNumber + 1 ) % catchUp.count  // catchUp总共只有4个
        
        // 创建更新通知的内容
        let updatedContent = createNotificationContent()
        
        // 更新内容(根据stepNumber的值去数组catchUp中取出对应的值)
        updatedContent.body = catchUp[stepNumber]
        
        // 更新userInfo
        updatedContent.userInfo["index"] = stepNumber
        
        // 更新子标题
        updatedContent.subtitle = request.content.subtitle
        
        // 创建通知请求,并且将其添加到通知中心
        addNotification(request.identifier, updatedContent, request.trigger)
    }
}

简单的来说一下上面的代码。因为我们要通过点击“更新即将显示的通知”按钮来改变通知中心里面已经存在,但是还未展示出来通知的内容,所以我们定义了一个字符串儿数组catchUp,它里面存放的字符串儿在我们后面更改通知内容的时候要用到。renewNextNotification(_ : )这个方法就是点击按钮时执行的代码,而updateNotification(_ : )这个方法就是具体执行更改通知内容的代码。先重复点击“又发通知”按钮,然后快速点击“查看等待通知”按钮,看看通知中心有多少条等待展示的通知,并且这些通知的具体内容是什么:

通知中心等待展示的通知及其内容.png

从控制台打印出来的结果来看,通知的内容全部为“再不起床,你老婆真跟人跑了!😂😂😂😂😂”。接下来要快速点击“更新即将显示通知的内容”按钮。注意!考验你手速的时候到了,一定要在通知被展示出来之前点击,因为一旦通知被展示出来了,再点击“更新即将显示通知的内容”按钮就没有效果了。并且,前面点击了多少次“又发通知”按钮,后面最好是点击多少次“更新即将显示的通知”按钮:

更新即将显示通知的内容.jpg

从上面通知展示出来的结果来看,3条通知的内容分别被修改为“不能让她跑了!赶紧追”、“还好来得及,赶紧追!”和“还有这种事?”。这几条内容都是从数组catchUp中随机取出来的。

四、移除通知

只要通知还未展示出来,我们就可以通过removePendingNotificationRequests(withIdentifiers identifiers: )函数将其从通知中心移除:

// MARK: - 移除通知
@IBAction func removeNotifications(_ sender: UIButton) {
    
    // 从通知中心获取所有还未展示的通知请求
    UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
        if let request = requests.first {
            
            // 根据所给的标识符,移除所有与之对应的还未展示的通知
            UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [request.identifier])
        }
        
        // 一次性移除所有的通知
        // UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    }
}

上面那个函数是通过通知的标识符来移除对应的通知,其实还可以用removeAllPendingNotificationRequests()函数一次性移除所有未经展示的通知。可以快速点击发送通知的按钮,然后点击“查看等待通知”按钮查看通知中心有多少个未经展示的通知,最后在通知展示之前点击“移除通知”按钮来移除它们:

移除通知中心里面未经展示的通知.png

五、可交互通知

从iOS 8之后,通知里面就可以进行交互了,不过在此之前你需要先进行注册,而且最好是在程序已启动时就进行相应的注册。来到AppDelegate文件中,先导入头文件UserNotifications,然后在application(_ : , didFinishLaunchingWithOptions : ) -> Bool这个函数中进行注册:

/// 设置通知的category
func setCategories() {
    
    /** 1、创建action */
    
    // 可输入文本的action
    let textInputAction = UNTextInputNotificationAction(identifier: "text.input", title: "回复", options: [], textInputButtonTitle: "发送", textInputPlaceholder: "请在这里输入文字进行回复")
    
    // 推送下一条
    let nextAction = UNNotificationAction(identifier: "next.action", title: "推送下一条", options: [])
    
    // 停止推送
    let stopAction = UNNotificationAction(identifier: "stop.action", title: "停止推送", options: [])
    
    // 稍后再推送
    let sentLaterAction = UNNotificationAction(identifier: "sent.later.action", title: "稍后推送", options: [])
    
    
    
    /** 2、创建category */
    
    // 创建第一个category
    let sendCategory = UNNotificationCategory(identifier: "send.category", actions: [stopAction], intentIdentifiers: [], options: [])
    
    // 创建第二个category
    let otherCategory = UNNotificationCategory(identifier: "other.category", actions: [nextAction, textInputAction, sentLaterAction, stopAction], intentIdentifiers: [], options: [])
    
    
    
    /** 3、在通知中心设置category */
    
    // 设置category
    UNUserNotificationCenter.current().setNotificationCategories([sendCategory, otherCategory])
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
    // 注册可交互通知
    setCategories()
    
    return true
}

回到ViewController文件中,分别在sendNotifications(_ : )和createNotificationContent() -> UNMutableNotificationContent函数中设置content的categoryIdentifier:

// 在sendNotifications(_ : )这个函数中设置categoryIdentifier
content.categoryIdentifier = "send.category"

// 在createNotificationContent() -> UNMutableNotificationContent中设置categoryIdentifier
content.categoryIdentifier = "other.category"

除了上述两个步骤之外,还要实现UNUserNotificationCenterDelegate中的userNotificationCenter(_ : , didReceive : , withCompletionHandler : )这个代理方法:

// 用于实现可交互的通知
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    
    // 取出actionIdentifier
    let action = response.actionIdentifier
    
    // 取出发送请求
    let request = response.notification.request
    
    // 如果用户点击了"推送下一条"按钮
    if action == "next.action" {
        
        // 调用更新即将显示通知内容的方法,更新下一条内容
        updateNotification(request)
    }
    
    // 如果用户点击了"停止推送"按钮
    if action == "stop.action" {
        
        // 将带有该条标识符的通知从通知中心移除
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [request.identifier])
    }
    
    // 如果用户点击了"稍后再推送"按钮
    if action == "sent.later.action" {
        
        // 创建触发器
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5.0, repeats: false)
        
        // 重新创发送通知的请求
        let newRequest = UNNotificationRequest(identifier: request.identifier, content: request.content, trigger: trigger)
        
        // 将通知发送请求添加到发送中心
        UNUserNotificationCenter.current().add(newRequest, withCompletionHandler: { (error) in
            if error != nil {
                // 如果有错误
                print("\(String(describing: error?.localizedDescription))")
            }
        })
    }
    
    // 如果用户点击了"回复"按钮
    if action == "text.input" {
        
        // 将接收到的response内容转换成UNTextInputNotificationResponse
        let textResponse = response as! UNTextInputNotificationResponse
        
        // 创建通知内容
        let newContent = request.content.mutableCopy() as! UNMutableNotificationContent
        
        // 将回复的内容设置为newContent的子标题
        newContent.subtitle = textResponse.userText
        
        // 将回复的内容作为通知内容添加通知中心
        addNotification(request.identifier, newContent, request.trigger)
    }
    
    // 函数回调
    completionHandler()
}

接下来,我们就可以运行程序进行相应的演示了。先点击“发送通知”按钮,演示比较简单的,因为它里面只有“停止推送”一个交互按钮:

简单交互通知的演示.jpg

上面那个通知比较简单,展开通知详情之后,它里面只有一个交互按钮,点击"停止推送"以后,系统就不会再推送通知了。接下来,我们要点击"又发通知"按钮,来演示更为复杂的交互通知:

复杂的交互通知.jpg

从上面通知展开的详情来看,它里面总过有4个可供交互的按钮,我们先来演示点击"推送下一条"action按钮,看看会发生什么情况:

点击“推送下一条”按钮.jpg

和上面那条通知的内容进行比较,当我们点击"推送下一条"按钮之后,系统会在5秒钟之后在推送一条通知,并且通知的内容会发生改变,这是因为我们在代理方法中调用了updateNotification()方法。接下来,我们点击"回复"action,看看会发生什么情况:

回复通知.jpg

当我们点击"回复"action按钮之后,系统会弹出输入键盘,输入相关的内容之后点击发送按钮,过一会儿系统又会将我们发送的文本作为通知的内容进行推送。最后,我们再来演示一下点击"稍后推送"action按钮:

点击“稍后推送”按钮.jpg

点击"稍后推送"action按钮之后,系统不会更改通知的任何内容,它会在5秒钟之后,将原通知原封不动的再推送一遍。当我们点击"停止推送"action按钮之后,系统不会再推送任何通知了,因为与该标识符有关的通知被从通知中心移除了。

六、多媒体通知

除了上面哪几种形式的通知意外,还可以推送音频通知、视频通知,以及图片通知(静态图片和动态图片都可以)。下面我们就来演示一下多媒体通知。

/// 加载音频或者视频资源
func notificationAttachment(_ identifier: String, _ resource: String, _ type: String) -> [UNNotificationAttachment] {
    
    // 创建音频或视频通知的标识符
    let extendedIdentifier = identifier + "." + type  // send.video.mp4
    
    // 从Bundle中获取资源所在的路径
    guard let path = Bundle.main.path(forResource: resource, ofType: type) else {
        print("找不到文件\(resource).\(type)!")
        
        // 返回空的数组
        return []
    }
    
    // 将资源路径转化为URL地址
    let multimediaURL = URL(fileURLWithPath: path)
    
    // 进行错误处理(先尝试操作,如果失败了,则进行错误处理)
    do {
        
        // 先尝试根据标识符和资源URL创建attachement
        let attachement = try UNNotificationAttachment(identifier: extendedIdentifier, url: multimediaURL, options: nil)
        
        // 返回创建成功的attachement
        return [attachement]
    }
    catch {
        print("attachement加载失败!")
        
        // 如果创建失败,则返回空的数组
        return []
    }
}


// MARK: - 发送通知
@IBAction func sendNotifications(_ sender: UIButton) {
    
    // 如果获取到发送通知的权限
    if isGrantedNotificationAccess {
        
        // 创建通知的内容
        let content = UNMutableNotificationContent()
        
        // 设置通知默认提示音
        content.sound = UNNotificationSound.default()
        
        // 设置通知的标题
        content.title = "难念的经"
        
        // 设置通知的内容
        content.body = "古装群像·香港电影·红颜乱入版"
        
        // 设置categoryIdentifier
        content.categoryIdentifier = "send.category"
        
        /** 加载多媒体通知 */
        
        // 将标识符、文件名称和扩展名作为参数传递进去
        let attachement = notificationAttachment("send.video", "难念的经", "mp4")
        
        // 设置内容的attachments
        content.attachments = attachement
        
        /** 创建通知触发器 */
        let dateComponents: Set<Calendar.Component> = [.second, .minute, .hour]
        var date = Calendar.current.dateComponents(dateComponents, from: Date())
        date.second = date.second! + 3
        let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: false)
        
        // 创建发送通知请求的标识符
        let identifier = "message.yourWifeRanAway"
        
        // 创建发送通知其的请求,并且将其添加到通知中心
        addNotification(identifier, content, trigger)
    }
}

需要注意的是,在使用UNNotificationAttachment创建attachement的过程中,可以在其options参数中设置视频的某一帧作为通知的封面,这一功能待会在后面设置GIF动画通知时再做演示。我们先来看一下视频通知的效果:

视频通知的推送.jpg

视频通知在推送时,其最右边会有一个图片预览,而这张预览图片可以通过UNNotificationAttachment在创建attachement对象时的参数options来控制。比如说设置该值为[UNNotificationAttachmentOptionsThumbnailTimeKey: 10],其中10表示缩略图的时间键值,可以理解为视频中的第10帧。点击视频的播放按钮就可以播放了:

视频通知播放演示.gif

点击视频中的任意位置就可以停止。除了可以推送视频通知意外,还可以推送音频通知。推送音频通知的方法和推送视频通知是几乎是一毛一样的。所以在点击"发送通知"按钮的实现代码中,修改content的相关属性,并且在创建attachement的时候,将音频文件的标识符、文件名称和扩展名传递过去:

// 设置通知的标题
content.title = "The Sound of Silence"

// 设置通知的内容
content.body = "Paul Simon & Garfunkel"

// 设置categoryIdentifier
content.categoryIdentifier = "send.category"

/** 加载多媒体通知 */

// 将标识符、文件名称和扩展名作为参数传递进去
let attachement = notificationAttachment("send.music", "The Sound of Silence", "mp3")

点击"发送通知"按钮,等到通知推送以后,点击详情就可以播放音频文件了:

播放音频通知.jpg

我们接下来要推送图片通知。先来看下一GIF图片通知如何实现:

// 动图通知
func setGif() -> [UNNotificationAttachment] {
    
    // 创建通知标识符
    let extendedIdentifier = "set.gif"
    
    // 从Bundle中获取资源所在的路径
    guard let path = Bundle.main.path(forResource: "动一动04", ofType: "gif") else {
        print("找不到文件!")
        
        // 返回空的数组
        return []
    }
    
    // 如果成功的加载到资源
    let gifURL = URL(fileURLWithPath: path)
    
    do {
        
        // 先尝试根据标识符和资源URL创建attachement(并且设置GIF的某一帧作为通知封面)
        let attachement = try UNNotificationAttachment(identifier: extendedIdentifier, url: gifURL, options: [UNNotificationAttachmentOptionsThumbnailTimeKey: 11])
        
        // 返回创建成功的attachement
        return [attachement]
    }
    catch {
        print("attachement加载失败!")
        
        // 创建失败则返回空的数组
        return []
    }
}

// MARK: - 又发通知
@IBAction func otherNotifications(_ sender: UIButton) {
    
    // 如果获取到发送通知授权
    if isGrantedNotificationAccess {
        
        // 调用createNotificationContent
        let content = createNotificationContent()
        
        // 创建通知触发器
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2.0, repeats: false)
        
        // 创建发送通知请求的标识符
        let identifier = "message.yourWifeRanAgain"
        
        // 创建发送通知其的请求,并且将其添加到通知中心
        addNotification(identifier, content, trigger)
    }
}

/// 创建通知的内容
func createNotificationContent() -> UNMutableNotificationContent {
    
    // 创建通知内容
    let content = UNMutableNotificationContent()
    
    // 设置通知默认提示音
    content.sound = UNNotificationSound.default()
    
    // 设置通知标题
    content.title = "来,屁股扭起来!"
    
    // 设置通知内容
    content.body = "脖子扭扭, 屁股扭扭, 早睡早起,咱们来做运动!😂😂😂"
    
    // 设置categoryIdentifier
    content.categoryIdentifier = "other.category"
    
    // 设置attachments,用于推送GIF通知
    content.attachments = setGif()
    
    return content
}

  点击“又发通知”按钮,然后再展开通知详情进行查看:

推送gif图片通知.gif

除了可以推送GIF图片通知以外,我们还可以推送JPG图片通知。下面,我们先来实现一个加载JPG图片资源并创建UNNotificationAttachment数组对象的函数,然后在updateNotification(_ : )函数中调用它,并且将其设置为content的attachments:

/// JPG图片通知
func setImages(_ step: Int) -> [UNNotificationAttachment] {
    
    // 根据传递进来的步长创建字符串
    let stepString = String(format: "%i", step)
    
    // 设置每一条图片通知的标识符
    let identifier = "set.image." + stepString
    
    // 拼接图片的名称
    let resource = "跑了0" + stepString
    
    // 图片的扩展名
    let type = "jpg"
    
    // 调用notificationAttachment()函数并返回
    return notificationAttachment(identifier, resource, type)
}

/// 更新即将显示通知的内容
func updateNotification(_ request: UNNotificationRequest) {
    
    // 获取所有标识符前缀为"message.yourWifeRanAgain"的请求
    if request.identifier.hasPrefix("message.yourWifeRanAgain") {
        
        // 根据userInfo中的键取出对应的值,并且将其强转成真实的类型
        var stepNumber = request.content.userInfo["index"] as! Int
        
        // 取余运算,能保证stepNumber的值永远都在有效的范围之内,可以防止数组越界
        stepNumber = (stepNumber + 1 ) % catchUp.count  // catchUp总共只有4个
        
        // 创建更新通知的内容
        let updatedContent = createNotificationContent()
        
        // 更新内容(根据stepNumber的值取数组catchUp中取出对应的值)
        updatedContent.body = catchUp[stepNumber]
        
        // 更新userInfo
        updatedContent.userInfo["index"] = stepNumber
        
        // 更新子标题
        updatedContent.subtitle = request.content.subtitle
        
        
        /** 设置JPG图片通知 */
        updatedContent.attachments = setImages(stepNumber)
        
        // 创建通知请求,并且将其添加到通知中心
        addNotification(request.identifier, updatedContent, request.trigger)
    }
}

  运行程序,点击“又发通知”按钮进行相应的操作:

推送JPG图片通知.gif

  至此,我们本地通知的演示就搞完了,我也懒得检查了😂😂😂😂😂,代码参见UserNotificationsDemo.

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

推荐阅读更多精彩内容