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
改成
|
|
这样就可以在 widget 中 使用
|
|
获得在主 app 中持久化的数据了。关于 app groups 其他的用法,可以继续深入研究。
widget 的折叠和展开
苹果的官方文档里面明确的说了,widget 的界面是不能滑动的。毕竟 widget 和通知中心的滑动不能冲突啊。
所以有时候我们需要将 widget 折叠起来,毕竟太长的 widget 实在是令人讨厌啊。
主要还是说说iOS10 上怎么做的吧,毕竟没有iOS10 以下的设备。
在 TodayViewController 的 didLoad 中添加
|
|
然后实现 NCWidgetProviding
协议中的方法
|
|
在 iOS8 和 iOS9 中, 由于系统没有这个功能。我们只能自己写一个按钮然后再来做这些事情了。
widget 打开 主app
widget 打开主 app 还是老思路,openurl 就可以了,然后在url 中添加对应需要的参数。
准备工作
主app -> target -> info -> UrlTypes
添加一个 URlType 然后设置 URL Scheme 为你自定义的字符串。 比如 “sunny”。
在 widget 中需要跳转的地方写这样的代码
|
|
参数传递也就是按照上文, 在url中拼接了。上文有提到, widget 和 app 可以共享数据。这也可能是一种传递参数的方式。
这个时候打开主要 app 就是直接进入主要界面了。如果我们需要做一些其他的事情应该怎么做呢?
想想以前做微信或者支付宝支付的时候, 都要在 appdelegate 中写一些代码。
|
|
others
高度
widget的默认高度是有限制的。
compact 下:
- max = 110
- mim = 110
expanded 下:
- min = 110
- max = 根据不同的机型二不同。
无论怎么设置, 都不回超出这个范围
widgetPerformUpdate
|
|
这个方法用来选择 widget 再出现的时候会不会重新刷新。
通知
在 NSExtensionContext
中看到的几个通知貌似不是给 TodayExtension 用的。
NSExtensionContext
中能看到几个通知他们都是监听 host app 的状态的。所以对于widget 来说, host app 就是 Today 这个东西啦。
最后
抛砖引玉,本文用Today Extension做了一个很简单的功能。 当然, 我们能用他做的事情可不止这些。这就需要我们发动我们的聪明才智了。
示例代码下载链接由于使用swift写的, 由于众所周知的原因, 你发现编译不过了。可以联系我, 我将做适配。