美文网首页iOS常用
[iOS] 通知详解: iOS 10 UserNotificat

[iOS] 通知详解: iOS 10 UserNotificat

作者: 流火绯瞳 | 来源:发表于2018-11-06 15:43 被阅读180次

    通知相关系列文章
    iOS10 之前通知使用介绍
    [iOS] 通知详解: UIUserNotification
    iOS10 相关API
    [iOS] 通知详解:iOS 10 UserNotifications API
    iOS10 本地/远程通知
    [iOS] 通知详解: iOS 10 UserNotifications
    iOS10 通知附加包
    [iOS] 通知详解: iOS 10 UserNotifications -- 附加包Media Attachments
    iOS10 自定义UI
    [iOS] 通知详解: iOS 10 UserNotifications -- 自定义通知UI

    新建 Notification content extension

    通知UI的自定义使用到了Notification content extension,同创建Notification Service Extension一样,我们需要创建一个新的 Target ,只不过这次选择Notification content extension

    下一步,为这个Target起一个名字,完成即可!

    可以看到多了下面几个文件:


    这里的NotificationViewController就是我们编写自定义UI逻辑的控制器,他和一般的控制器一样,MainInterface.storyboard是与其绑定的,可以在此往storyboard添加控件。Info.plist为其相关的配置文件,有些操作需要在这里配置一些设置后,才能看到预期的效果,下面关于此部分的所有配置,都是在这里进行的。

    NotificationViewController中,实现了UNNotificationContentExtension协议,他有两个协议方法

    // 必须实现,用来处理自定义UI的内容
    public func didReceive(_ notification: UNNotification)
    // 选择实现,用来处理action的事件
    optional public func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void)
    
    

    第一个是必须要实现的,在NotificationViewController默认已经实现了,主要是处理当通知来的时候,布局自定义的UI内容以及相关的处理逻辑的地方;
    第二个方法,当前发送的通知带有快捷操作action的时候(UNNotificationAction),来处理相关的点击事件。

    因为我们自定义的任何View都是无法交互的,只能借助添加的action来处理相关的事件。

    绑定 Category

    Notification content extension添加完成后,在通知界面是看不到我们自定义的UI的,还需要绑定相关的 Category,即在创建通知的时候,我们添加的UNNotificationCategory,如果没有需要交互的action,可以传个空数组:

    let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
            UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
    
    

    然后在该扩展下的Info.plist中添加该Categoryidentifier,对应着UNNotificationExtensionCategory字段:

    注意:这里的UNNotificationExtensionCategory可以修改为数组类型,如果我们有多个Category公用一套UI,可以将此值修改为Array类型,然后在数组里添加多个 Category 的identifier。

    再去发送通知,注意此时的Payload中要添加category字段:

    {
    "aps":
        {
            "alert":
            {
                "title":"iOS10远程推送标题",
                "subtitle" : "iOS10 远程推送副标题",
                "body":"这是在iOS10以上版本的推送内容,并且携带来一个图片附件"
            },
    
    "category":"categoryidentifier",
            "badge":1,
            "mutable-content":1,
            "sound":"default",
    "image":"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3078873712,1340878922&fm=26&gp=0.jpg"
            
        }
    }
    
    

    弹框和锁屏页显示的内容和之前一样,打开通知或者下拉弹框,就会看到我们自定义的页面了:

    比较丑的那部分就是我们自定义的UI了,可以看到真的很丑,大小还不合适,而且和系统默认的也重复的。

    如果我们想要隐藏系统默认的内容页面,也就是下面的那部分,头是隐藏不了的;只需要在Info.plist里添加字段UNNotificationExtensionDefaultContentHidden,bool类型并设置其值为YES;

    关于页面太大的问题,有的说通过修改其宽高比UNNotificationExtensionInitialContentSizeRatio的值,如果你的UI是固定的,可以通过适配大部分屏幕后,通过修改此值来得到合适的宽高比视图,但其值也是需要各种尝试的。
    另外也可以使用autolayout,如果是在storyboard里添加的实图,顺便添加相应的约束即可;然后重新发送消息,大概就是这个样子:

    这样,通知页面会先显示一个大的页面,然后再resize到约束后的页面大小,这样就会一个缩放的动画,这是因为在通知即将展示的时候,系统还没有调用我们的约束代码,也就是约束还没有起效,所以会有个resize的动画过渡。
    为解决这个问题,只能在自定义UI的时候配合UNNotificationExtensionInitialContentSizeRatio设置合适页面大小,即采用固定的样式来展示通知内容。

    显示附加包(attachment)的内容

    如果我们的通知是携带附加包的,例如一张图片,添加自定义的UI后,打开通知或者下拉弹框会发现,大图不显示了,我们可以把相关的内容显示到自定义的UI上,还是以图片为例,在didReceive方法里添加以下获取附加包数据的代码:

    if let att = notification.request.content.attachments.first {
    
                if att.url.startAccessingSecurityScopedResource() {
                    self.coverImage.image  = UIImage(contentsOfFile: att.url.path)
                    att.url.stopAccessingSecurityScopedResource()
                }
            }
    
    

    这里需要说一下startAccessingSecurityScopedResourcestopAccessingSecurityScopedResource方法:
    因为attachment是由系统单独管理的,所以这里我们在使用attachment之前,需要告诉iOS系统,我们需要使用它,并且在使用完毕之后告诉系统我们使用完毕了。对应上述代码就是startAccessingSecurityScopedResource()和stopAccessingSecurityScopedResource()的操作。当我们获取到了attachment的使用权之后,我们就可以使用那个文件获取我们想要的信息了。

    再去发送上面的Payload,打开后就是这样了:

    意思是那么个意思,但是加载的图片好像不太完整,上面我们是从attachment里面获取的,目前不清楚出现这个情况的原因,可能原数据被压缩了导致数据不全。所以,我们可以从发送的Payload中来获取数据:

    if let aps = notification.request.content.userInfo["aps"] as? [String: Any] {
    
                if let imagePath = aps["image"] as? String {
    
                    if let url = URL(string: imagePath) {
    
                        if let data = try? Data.init(contentsOf: url) {
    
                            self.coverImage.image = UIImage(data: data)
                        }
                    }
                }
            }
    

    这样就能正常显示了:

    处理action事件

    如果我们添加的category是带有action的,并且action的点击事件要响应到我们自定义的UI里面,例如点击的时候更换一个图片, 就需要UNNotificationContentExtension协议的另一个协议方法了:

    // response:可以拿到点击的action,和通知的内容
    // completion:处理完成后需要告诉系统,接下来该如何处理该通知
    optional public func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void)
    
    

    UNNotificationContentExtensionResponseOption 是一个枚举,他有三个值:

    @available(iOS 10.0, *)
    public enum UNNotificationContentExtensionResponseOption : UInt {
    
        // 通知页面不会消失,例如更新UI,显示出来
        case doNotDismiss
    // 关闭当前通知页面
        case dismiss
    // 将此action事件传递给app,在通知中心的代理方法里继续处理该事件
        case dismissAndForwardAction
    }
    
    

    需要注意的是,如果实现了此方法,就需要对所有添加的action进行处理,而不能只处理某个action

    例如我们这样处理点击事件:

    func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
    // 改变标题
            self.label?.text = self.label?.text ?? "" + "点击了 "
            
            if response.actionIdentifier == "okidentifier" {
                // 点击了查看按钮,这里改变了标题的颜色
    
                self.label?.textColor = UIColor.red
                
                completion(.doNotDismiss)
            } else if response.actionIdentifier == "cancelidentifier" {
                // 点击了关闭,直接关闭通知
                completion(.dismiss)
            } else {
                // 如果还有其他的按钮,交给app处理
                completion(.dismissAndForwardAction)
            }
        }
    

    然后,在创建通知的时候,添加相应的action:

    let okAction = UNNotificationAction(identifier: "okidentifier", title: "查看", options: UNNotificationActionOptions.foreground)
       
            let cancel = UNNotificationAction(identifier: "cancelidentifier", title: "关闭", options: UNNotificationActionOptions.destructive)
            
            let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [okAction, cancel], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
            UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
    

    再次发生Payload,点击通知的查看action,会发现标题和标题的颜色都修改了。

    处理快捷回复(输入文字)

    前面知道,我们可以在通知中心进行快捷回复,只需要创建UNTextInputNotificationAction的action,添加到对应的category即可:

    let okAction = UNTextInputNotificationAction(identifier: "okidentifier", title: "回复", options: .foreground, textInputButtonTitle: "快捷回复", textInputPlaceholder: "请输入。。。")
            
            
            let cancel = UNNotificationAction(identifier: "cancelidentifier", title: "关闭", options: UNNotificationActionOptions.destructive)
            
            let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [okAction, cancel], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
            UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
    
    

    这里,我们需要这样来处理接收到的反馈:

    func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
            self.label?.text = "点击了 "
            
            if response.actionIdentifier == "okidentifier" {
                
    
                // 这里处理输入框的事件
                if let txRes = response as? UNTextInputNotificationResponse {
                    // 如果是输入框的反馈,获取输入内容,显示出来
                    let text = txRes.userText
                    self.label?.text = text
                }
                
                // 点击了查看按钮,这里改变了标题的颜色
                self.label?.textColor = UIColor.red
                
                completion(.doNotDismiss)
            } else if response.actionIdentifier == "cancelidentifier" {
                // 点击了关闭,直接关闭通知
                completion(.dismiss)
            } else {
                // 如果还有其他的按钮,交给app处理
                completion(.dismissAndForwardAction)
            }
        }
    

    然后在通知中心点击回复按钮的时候会弹出输入框,输入结束后,通知中心即显示了输入的内容:

    到此,断断续续,总算是把通知相关的内容整体过了一遍,虽然感觉上还是有些逻辑混乱,基本上能够体现通知的一些使用方法。
    参考文章
    iOS10-UserNotifications
    WWDC2016 Session笔记 - iOS 10 推送Notification新特性

    相关文章

      网友评论

        本文标题:[iOS] 通知详解: iOS 10 UserNotificat

        本文链接:https://www.haomeiwen.com/subject/ydeuxqtx.html