iOS小组件 - iOS 17 交互性小组件
iOS 17 上苹果为小组件新增了两个交互性组件 Botton / Toggle. 通过这两个 View, 我们可以直接在小组件中执行某些 App 的功能.
其内部基于 AppIntent 框架实现, 核心逻辑是:
- App 基于 AppIntent 给系统暴露一个可执行的功能
- 小组件中交互组件点击, 系统执行 App 暴露的功能
具体实现步骤如下:
创建组件
创建一个小组件是基本操作, 步骤如下, 细节不再赘述
File -> New Target -> widget extension
封装一个 Intent 结构, 暴露 App 方法给系统
自定义一个结构体, 遵守 AppIntent 协议.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import Foundation
import AppIntents
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
struct ClickCountIntent: AppIntent {
static let intentClassName = "ClickCountIntent"
static var title: LocalizedStringResource = "Click Count Intent"
static var description = IntentDescription("record click count")
@Parameter(title: "Selected Hero")
var count: String?
init(count: String? = nil) {
self.count = count
}
init() {
count = ""
}
func perform() async throws -> some IntentResult {
print(self.count ?? "这里可以执行某个 App 的方法")
/*
AppIntent 返回之前要存档好所有必要数据, 因为一返回就会直接刷新 timeline
*/
let count = UserDefaults.standard.integer(forKey: "count")
UserDefaults.standard.setValue(count + 1, forKey: "count")
return .result()
}
}
小组件部分支持 iOS 17 新的交互组件
iOS 17 新交互组件有 Button / Toggle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct widgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
// 以 Button 为例, 记录点击次数
let count = UserDefaults.standard.integer(forKey: "count")
Button(intent: ClickCountIntent(count: "已经点击次数 \(count)")) {
if count > 0 {
Text("Click Me (\(count))")
.foregroundStyle(.yellow)
}else{
Text("Click Me")
.foregroundStyle(.yellow)
}
}.tint(.clear)
}
}
}
一些细节
Intent 的 perform()
函数执行完系统会立即刷新时间线, 此刻处理的数据要存档, 避免小组件读取的时候信息丢失
因为 perform()
函数执行完到刷新时间线重新绘制 UI 有一小段延迟, 为了优化体验, 苹果提供了 .invalidatableContent()
修饰符来修饰某个视图,这样在点击按钮之后到 UI 更新之前,更新内容都是无效的。
本文由作者按照 CC BY 4.0 进行授权