iOS Widget小记

项目最近在调研是否需要为项目添加一个 Widget。这篇文章记录下自己学习 Widget 的过程。

  • 创建 Widget
  • 与主 App 的交互
    • 数据共享

创建 Widget

Widget 是一个依附于主 App 的插件,所以如果你想创建对应项目的 Widget 的话,在对应的项目中,新建一个target,类型选择 Today Extension。

创建

File -> New -> Target -> Today Extension 创建 Widget。

按照上述步骤创建完成之后,你的项目目录应该是下面图所示:

开发方式

Widget 界面的开发是可以使用纯代码或者 Storyboard 的,这个依据个人喜好了。

Widget 创建之后默认是使用 Storyboard 的,若需要纯代码的话,只需要到上图中 Widget 目录下的 Info.plist 中,删除如下黄框中的字段,并加入红框中的键值对就?了。

代码编写

这里我使用了 Storyboard 来开发 Widget 界面。

在 TodayViewController 中,开始布局 Widget 的样式。通用组件都可以使用,但是 UITableView 等滚动视图是无法滚动的。

iOS10 之后,Widget 支持展开及折叠两种展现方式,通过设置 widgetLargestAvailableDisplayMode 属性可以让 Widget 程序实现展开布局,同时在左滑到 Widget 显示的时候,会调用 viewWillAppear,这时候可以去刷新数据获取最新的数据。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // .compact 无法折叠
    // .expanded 可以折叠
    if #available(iOS 10.0, *) {
        self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded
    }

    self.fetchData()
}
复制代码

Widget 默认的 mode 是 compact,当我们设置了 expanded 之后才会有折叠选项。

右上角折叠与收起的回调,我们可以在下面的方法中处理

// 展开 / 折叠回调
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
    if activeDisplayMode == .compact {
        // 高度固定,最低高度为110
        self.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 200)
    } else {
        self.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 500)
    }
}
复制代码

与主 App 交互

Widget 的目的就是使用户能够快速访问一些主 App 中的信息,当然其中也可以进行一些事件的处理,但是一般事件处理的方式都是调起主 App 来处理。为了能让 Widget 能和主 App 进行这些数据的共享,我们还需要进行一些相应的配置。

证书配置

ps: 需给 Widget 创建新的 AppID,Bundle ID 需要以主 App 的 Bundle ID 为前缀!

  1. 登录开发者账号,创建 App Groups。
  2. 进入到主 App 的 App ID 配置刚创建的 App Group。
  3. 进入到 Widget 的 App ID 配置 App Group。
  4. 进入 Xcode,分别进入主 App 及 Widget 的 target Capabilities,开启 App Group。 添加对应的 App Group。

这样我们的准备工作就做好了。

数据共享

Widget 给主 App 传值

Widget 中处理事件一般都是调起主 App,Widget 调起主 App 就相当于一个 App 调起另一个 App 的过程,所以我们这里我们需要分两步:

  1. 在主 App 的 target 中,Info -> URL Types,配置主 App 的 URL Scheme。

  1. 在 Widget 相应位置中,利用 Scheme 调起主程序。
// 打开主App
@IBAction func cleanBtnClicked(_ sender: UIButton) {
    self.extensionContext?.open(URL(string: "LearnWidgetDemo://hello")!, completionHandler: { (successful) in
        print("打开成功")
    })
}
复制代码

当然 Scheme 里面也可以传值,然后在主 App 的 Appdelegaete 的回调中处理:

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    // handle
    return true
}
复制代码
主 App 与 Widget 数据共享

上面的配置,主要就是为了让 Widget 能获取到主 App 的数据。

数据共享的方式有两种:

  • UserDefaults: 适合小量存储。
  • FileManager: 数据量较大时。

两种方式都可以写入和读取,一般情况下,都是主 App 写入,Widget 读取显示。

UserDefaults

因为沙盒机制,Widget 是不允许访问主 App 的沙盒路径的,因此 UserDefaults 的获取方法与平常有所区别,需要搭配 App group 完成实例化 UserDefaults。

// 这种方式是不行的
// let userDefault = UserDefaults.standard

// 获取实例
let userDefault = UserDefaults(suiteName: "App Group Name")
复制代码
FileManager

同样的,FileManager 也有特殊的获取方式:

// 这种方式是不行的
//let fileManager = FileManager.default

// 获取实例
let fileManeger = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "App Group Name")

复制代码

总结: 上述两种数据共享的方式,除了获取实例时,需要制定 App Group Name,其它数据存取方式都跟正常使用时一样。


ps: 如果你想测试,需要用模拟器去测试,因为如果你这个之前没有发过,就算能安装成功,手机上也没有显示的,但是模拟器上可以。


参考: App Extension Essentials —— Today

转载于:https://juejin.im/post/5d2be0ece51d4577523f2430

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值