Swift - UserNotifications框架使用详解7(自定义通知详情视图)

iOS 10 中添加了两个与通知相关的 extension:Service Extension 和 Content Extension。前者我在上文中已经介绍过了,本文介绍下后者:可以用来自定义通知的详细页面视图的 Content Extension。

十五、创建 Notification Content Extension

1,操作步骤

(1)首先我们点击"File" -> "New" -> "Target...",使用 NotificationContent 的模板来创建一个 NotificationContent。

(2)使用模版创建完毕后,会自动生成如下三个文件:

(3)NotificationViewController.swift

这个是一个实现了 UNNotificationContentExtension 的 UIViewController 子类。

该 extension 中有一个必须实现的方法 didReceive(_:)。当系统需要显示自定义样式的通知详情视图时,这个方法将被调用,然后我们可以在其中配置更新我们的 UI。

默认生成的代码如下:直接将通知内容显示在 label 中。我们先不做修改。

import UIKit

import UserNotifications

import UserNotificationsUI


class NotificationViewController: UIViewController, UNNotificationContentExtension {


    @IBOutlet var label: UILabel?


    override func viewDidLoad() {

        super.viewDidLoad()

    }


    func didReceive(_ notification: UNNotification) {

        self.label?.text = notification.request.content.body

    }

}

(4)MainInterface.storyboard

这里定义了该 extension 对应的详情视图 UI。

默认生成的 UI 如下:只有一个 label 文本标签。我们先不做修改。

(5)Info.plist

这里指定了该 extension 的各种配置。我们可以通过 Info.plist 控制通知详细视图的尺寸,以及是否显示原始的通知。

要特别注意的是 UNNotificationExtensionCategory 这个 key 值,它指定这个通知样式所对应的 category 标识符。系统在接收到通知后会通过 category 标识符先查找有没有能够处理这类通知的 content extension,如果存在,那么就交给这个extension 来进行处理。

默认生成的 category 标识符是 myNotificationCategory。我们先不做修改。

2,使用样例

(1)下面代码在页面加载完毕后会自动创建个 5 秒后的推送通知。注意的是我们将通知的 categoryIdentifier 设置成上面 content extension 的 category 标识符。

import UIKit

import UserNotifications


class ViewController: UIViewController {


    override func viewDidLoad() {

        super.viewDidLoad()


        //设置推送内容

        let content = UNMutableNotificationContent()

        content.title = "hangge.com"

        content.body = "做最好的开发者知识平台"


        //设置category标识符

        content.categoryIdentifier = "myNotificationCategory"


        //设置通知触发器

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)


        //设置请求标识符

        let requestIdentifier = "com.hangge.testNotification"


        //设置一个通知请求

        let request = UNNotificationRequest(identifier: requestIdentifier,

                                            content: content, trigger: trigger)


        //将通知请求添加到发送中心

        UNUserNotificationCenter.current().add(request) { error in

            if error == nil {

                print("Time Interval Notification scheduled: \(requestIdentifier)")

            }

        }

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

}

(2)可以看到通知的详情视图已经变成我们自定义的视图了。

十六、自定义通知详情视图(并添加 UI 交互功能)

1,效果图

(1)这里我们推送一条使用自定义详情视图的通知,这个通知里其实包含了三条新闻资讯。

(2)打开通知详情时,会显示第一条资讯的标题、内容摘要、以及相关图片。下方还有三个交互按钮:

点击“换一条”按钮,这个通知视图会继续显示,不过内容会切换成下一条资讯。

点击“打开”按钮,则自动打开应用。我们可以在程序中进行下一步操作,比如打开对应的新闻详情页面。

点击”取消“按钮,则自动清除通知,且不会打开应用。

2,Notification Content Extension 配置

(1)MainInterface.storyboard

这里我们在视图上添加两个 Label 和一个 ImageView,分别用来显示新闻资讯的标题、内容摘要、以及图片。

同时还要设置好各个组件的约束,并在代码中做好 @IBOutlet 关联。

(2)NotificationViewController.swift

我们在这里对详情视图进行显示和更新操作。特别注意的是,虽然我们可以使用包括按钮在内的各种 UI 组件,但是系统不允许我们与这些 UI 进行交互。点击通知视图本身会直接将我们导航到应用中,因此我们需要通过 action 按钮的方式来对视图进行更新。

didReceive(_:completionHandler:)方法介绍:

它是 UNNotificationContentExtension 的一个可选方法,它会在用户选择了某个 action 时被调用。我们可以根据情况给 completionHandler 传递不同的值实现不同操作:

如果需要更新详情视图,可以选择传递 .doNotDismiss 来保持通知继续被显示。

如果没有继续显示的必要,传递 .dismissAndForwardAction 会打开应用,并把通知的 action 继续传递给应用的 UNUserNotificationCenterDelegate 中的 userNotificationCenter(:didReceive:withCompletionHandler)

而传递 .dismiss则直接将这个通知清除,同时也不会打开这个应用。

import UIKit

import UserNotifications

import UserNotificationsUI


//资讯条目

struct NewsItem {

    let title: String

    let abstract: String

    let url: URL

}


class NotificationViewController: UIViewController, UNNotificationContentExtension {


    //显示资讯标题

    @IBOutlet var titleLabel: UILabel!


    //显示资讯内容摘要

    @IBOutlet weak var abstractLabel: UILabel!


    //显示资讯图片

    @IBOutlet weak var imageView: UIImageView!


    //当前显示的资讯索引

    private var index: Int = 0


    //所有资讯条目

    var items: [NewsItem] = []


    override func viewDidLoad() {

        super.viewDidLoad()

    }


    //收到通知

    func didReceive(_ notification: UNNotification) {

        //处理资讯条目

        let content = notification.request.content

        if let news = content.userInfo["news"] as? [[String: String]] {

            for i in 0..<news.count {

                let title = news[i]["title"] ?? ""

                let abstract = news[i]["abstract"] ?? ""

                let url = content.attachments[i].url

                let presentItem = NewsItem(title: title, abstract: abstract, url: url)

                self.items.append(presentItem)

            }

        }


        //显示第一条资讯

        updateNews(index: 0)

    }


    //更新显示的资讯内容

    private func updateNews(index: Int) {

        let item = items[index]


        //更新标题和内容摘要

        self.titleLabel!.text = item.title

        self.abstractLabel.text = item.abstract


        //更新图片

        if item.url.startAccessingSecurityScopedResource() {

            self.imageView.image = UIImage(contentsOfFile: item.url.path)

            item.url.stopAccessingSecurityScopedResource()

        }


        self.index = index

    }


    //Action按钮点击响应

    func didReceive(_ response: UNNotificationResponse, completionHandler completion:

        @escaping (UNNotificationContentExtensionResponseOption) -> Void) {

        if response.actionIdentifier == "change" {

            //切换下一条资讯

            let nextIndex = (index + 1) % items.count

            updateNews(index: nextIndex)

            //保持通知继续被显示

            completion(.doNotDismiss)

        } else if response.actionIdentifier == "open" {

            //取消这个通知并继续传递Action

            completion(.dismissAndForwardAction)

        } else if response.actionIdentifier == "dismiss" {

            //直接取消这个通知

            completion(.dismiss)

        } else {

            //取消这个通知并继续传递Action

            completion(.dismissAndForwardAction)

        }

    }

}

(3)extension 的 Info.plist

通知扩展对应的 category 标识符这里不做修改。主要修改下 UNNotificationExtensionInitialContentSizeRatio 这个 key 值,它是 UI 界面默认的高宽比,将其修改成 0.8,这样在界面出来的时候不会有很突兀的 frame 改变。

3,Notification Content Extension 使用

(1)AppDelegate.swift

这里要注意的是我们注册一个通知 category,里面包含三个 Action 按钮。

import UIKit

import UserNotifications


@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {


    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions

        launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {


        UNUserNotificationCenter.current()

            .requestAuthorization(options: [.alert, .sound, .badge]) {

                (accepted, error) in

            if !accepted {

                print("用户不允许消息通知。")

            }

        }


        //注册category

        registerNotificationCategory()


        return true

    }


    func applicationWillResignActive(_ application: UIApplication) {

    }


    func applicationDidEnterBackground(_ application: UIApplication) {

    }


    func applicationWillEnterForeground(_ application: UIApplication) {

    }


    func applicationDidBecomeActive(_ application: UIApplication) {

    }


    func applicationWillTerminate(_ application: UIApplication) {

    }


    //注册一个category

    private func registerNotificationCategory() {

        let newsCategory: UNNotificationCategory = {

            //创建三个普通的按钮action

            let changeAction = UNNotificationAction(

                identifier: "change",

                title: "换一条",

                options: [])


            let openAction = UNNotificationAction(

                identifier: "open",

                title: "打开",

                options: [.foreground])


            //创建普通的按钮action

            let cancelAction = UNNotificationAction(

                identifier: "cancel",

                title: "取消",

                options: [.destructive])


            //创建category

            return UNNotificationCategory(identifier: "myNotificationCategory",

                                          actions: [changeAction, openAction, cancelAction],

                                          intentIdentifiers: [], options: [])

        }()


        //把category添加到通知中心

        UNUserNotificationCenter.current().setNotificationCategories([newsCategory])

    }

}

(2)ViewController.swift

我们同样是页面打开后就推送个 5 秒后的通知。注意的是这里会给通知添加附加信息(包含资讯标题和内容摘要),以及资讯使用的图片附件。

import UIKit

import UserNotifications


class ViewController: UIViewController {


    override func viewDidLoad() {

        super.viewDidLoad()


        //设置推送内容

        let content = UNMutableNotificationContent()

        content.body = "今日资讯精选【2017-12-12】"


        //设置通知category标识符

        content.categoryIdentifier = "myNotificationCategory"


        //设置通知附件图片

        let imageNames = ["image1", "image2", "image3"]

        let attachments = imageNames.flatMap { name -> UNNotificationAttachment? in

            if let imageURL = Bundle.main.url(forResource: name, withExtension: "png") {

                return try? UNNotificationAttachment(identifier: "\(name)", url: imageURL,

                                                     options: nil)

            }

            return nil

        }

        content.attachments = attachments


        //设置通知附加信息(资讯标题和内容摘要)

        content.userInfo = ["news": [

            ["title": "全国人民喜迎油价上涨",

             "abstract": "据国内多家测评机构的分析,国内成品油零售限价将迎来“两连涨”..."],

            ["title": "房价同比下降城市大幅扩容",

             "abstract": "70个大中城市中一二三线城市房价同比涨幅继续回落。这意味着,往年..."],

            ["title": "比特币市值再创新高",

             "abstract": "一项名为SegWit2X的技术取消升级,导致在本周一比特币市值蒸发多达380亿美元..."]

            ]]


        //设置通知触发器

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)


        //设置请求标识符

        let requestIdentifier = "com.hangge.testNotification"


        //设置一个通知请求

        let request = UNNotificationRequest(identifier: requestIdentifier,

                                            content: content, trigger: trigger)


        //将通知请求添加到发送中心

        UNUserNotificationCenter.current().add(request) { error in

            if error == nil {

                print("Time Interval Notification scheduled: \(requestIdentifier)")

            }

        }

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

}

原文出自:www.hangge.com  转载请保留原文链接:https://www.hangge.com/blog/cache/detail_1855.html

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