面向 Extension 开发 🌞 Today Extension

习惯性美女做封面

App Extension 让我们在用户正在使用其他 App 的时候, 拓展我们 App 的功能。

Today Extension 也叫做 widget。 它能够让一些重要的消息更快速的到达你的用户。比如说, 用户可以通过它查看天气,或者股票价格, 查看日程表等等。苹果在官方文档中说到, 一个 widget 应该有以下的特点。

  • 确保内容是最新的
  • 响应的用户事件
  • 性能好(在iOS上占用大量内存,系统可能会kill掉这个widget)

创建 Today Extension

Xcode -> File -> New -> Target -> TodayExtension

跟创建一个新的项目一样, 设置创建好之后, 项目中会多一个 Target, 修改Scheme 为你刚刚创建的 Extension 再运行, 就能在 通知中心的 Today 里面看到你刚刚创建的 widget 了, 上面写着“Hello world”

另外 Xcode 给你创建了默认的模版文件。

  • TodayViewController.swift(如果是 OC 对应会是 .h.m 文件)
  • MainInterface.storyboard
  • Info.plist

注意: 默认是使用这个 storyboard 作为这个 widget 的入口。如果不需要使用storyboard 可以删除掉这个storyboard并且将Info.plist 中的

  • NSExtensionMainStoryboard 改成 NSExtensionPrincipalClass
  • MainInterface 改成 TodayViewController

设置界面

完成了上面的步骤之后, 不论你是选择用 stroyboard 作为你 widget 的入口, 还是选择用代码来做这件事情。都是一样的。

由于不知道什么原因, 我在网上看到的文章都是使用代码来做的这件事情。所以在这篇文章以及后面的示例代码中都将使用 Xcode 默认的 storyboard 来做这个 widget 的布局。

我将解决的问题

  • 在 widget 中打开主 App 并传递参数
  • widget 和 主 App 共享数据
  • widget 和 主 App 共用资源
  • widget 的打开和折叠

我遇到的坑

也没什么坑, 毕竟 Today Extension 并不是什么很难的东西。

  • 测试的时候, 由于 widget 和 主 App 是两个不同的 target, 所以在传递参数的时候, 在 appdelegate 中打印对应的值没有效果。最开始我还以为是因为设置的 scheme 是 widget 所以在 主 App 中的修改是无效的。但是实际是并不是这样。将参数以 alert 的形式表现出来, 这时候能够发现, 其实主 App 是跑起来了的。

先说说我做的准备工作吧

为了不扯那么多没用的东西。先说说我做了那些跟今天主题没什么关系的事情。

写主 App

在主 App 中我写了一个 UITableView, 并使用 Userdefault 将我要持久化的数据保存下来。然后对应给 Todo list 做了,添加,和删除的功能。

widget

在 widget 中我也下了同样的一个 UITableView 只有查看的功能。

要做的事情

widget 和 主 App 共用资源

widget 和主 App 共享代码和资源。作为一个工程师, 我们在任何事情的时候都要想到高类聚低耦合着句不变的真理。所以我们还是要尽可能的让 widget 和主 App 共享代码。

主要有两个方案:

  • framework
  • 直接共享

framework 的话,就拿 cocoapods 来说吧, 由于 widget 是一个新的target, 所以只需要在 podfile 中对应添加代码就能够在 widget 中使用。

另外一个是 直接共享, 这个就很简单了。我在示例中让主 App 和 widget 共享了一张图片,一个 TodoCell 类(包括xib 文件)。我做的唯一的一件事情就是在 Xcode 中选中这个文件,然后在 Xcode右边的 TargetMenberShip 中勾选对应的 target.

widget 和 主 App 共享数据

严格来说 widget 和 App 是不同的两个 App 了, 他们之间要共享数据的话只能使用 App Groups 了。

首先在主 App

target -> capabilities -> App Groups

打开 App Groups 功能, 点击 + , 设置 id 。如果重复了就改一个。

widget App

target -> capabilities -> App groups

这时候的 group 列表就能够看到对应的 group 了。勾选即可。

这时候已经完成了widget 和 主 App 共享数据的前提条件。

接下来还需要做的事情, 就是将我们准备工作里面Userdefault相关代码进行调整。

UserDefaults.standard 改成

UserDefaults(suiteName: "your group id")

这样就可以在 widget 中 使用

let userdefault = UserDefaults(suiteName: "group.com.sunny.group")

获得在主 App 中持久化的数据了。关于 App Groups 其他的用法,可以继续深入研究。

widget 的折叠和展开

苹果的官方文档里面明确的说了,widget 的界面是不能滑动的。毕竟 widget 和通知中心的滑动不能冲突啊。

所以有时候我们需要将 widget 折叠起来,毕竟太长的 widget 实在是令人讨厌啊。

主要还是说说iOS10 上怎么做的吧,毕竟没有iOS10 以下的设备。

在 TodayViewController 的 didLoad 中添加

        // iOS10 添加折叠按钮
        if #available(iOSApplicationExtension 10.0, *) {
            extensionContext?.widgetLargestAvailableDisplayMode = .expanded
        } else {
// iOS8 、iOS9 上需要自己添加折叠按钮
        }

然后实现 NCWidgetProviding 协议中的方法

    func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
// 由于 iOS8 、iOS9 上没有这个代理。需要对自己添加的按钮设置 target-action 然后进行修改
        switch activeDisplayMode {
        case .compact:
            preferredContentSize = maxSize
        case .expanded:
            preferredContentSize = CGSize(width: 0.0, height: 60 * CGFloat(dataSource.count))
        }
    }

在 iOS8 和 iOS9 中, 由于系统没有这个功能。我们只能自己写一个按钮然后再来做这些事情了。

widget 打开 主 App

widget 打开主 App 还是老思路,openurl 就可以了,然后在url 中添加对应需要的参数。

准备工作

主 App -> target -> info -> UrlTypes

添加一个 URlType 然后设置 URL Scheme 为你自定义的字符串。 比如 “sunny”。

在 widget 中需要跳转的地方写这样的代码

self.extensionContext?.open(NSURL(string: "sunny://action=\(dataSource[indexPath.row])")

参数传递也就是按照上文, 在url中拼接了。上文有提到, widget 和 App 可以共享数据。这也可能是一种传递参数的方式。

这个时候打开主要 App 就是直接进入主要界面了。如果我们需要做一些其他的事情应该怎么做呢?

想想以前做微信或者支付宝支付的时候, 都要在 appdelegate 中写一些代码。

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        let prefix = "sunny://"// 判断是否是可靠的地方传递过来的
        if url.absoluteString.hasPrefix(prefix) {
        // 参数过来了! 做对应的事情
            let a = UIAlertController(title: url.absoluteString, message: nil, preferredStyle: .alert)
            a.addAction(UIAlertAction(title: "取消", style: .cancel, handler: nil))
            self.window?.rootViewController?.present(a, animated: true, completion: nil)
            return true
        }
        return false
        
    }

others

高度

widget的默认高度是有限制的。

compact 下:

  • max = 110
  • mim = 110

expanded 下:

  • min = 110
  • max = 根据不同的机型二不同。

无论怎么设置, 都不回超出这个范围

widgetPerformUpdate
    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        // Perform any setup necessary in order to update the view.
        
        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData
        
        completionHandler(NCUpdateResult.newData)
    }

这个方法用来选择 widget 再出现的时候会不会重新刷新。

通知

NSExtensionContext 中看到的几个通知貌似不是给 TodayExtension 用的。

NSExtensionContext 中能看到几个通知他们都是监听 host App 的状态的。所以对于widget 来说, host App 就是 Today 这个东西啦。

最后

抛砖引玉,本文用Today Extension做了一个很简单的功能。 当然, 我们能用他做的事情可不止这些。这就需要我们发动我们的聪明才智了。

示例代码下载链接由于使用swift写的, 由于众所周知的原因, 你发现编译不过了。可以联系我, 我将做适配。

原文地址: 面向 Extension 开发 🌞 Today Extension

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

推荐阅读更多精彩内容