试试新框架 — TipKit

今年 WWDC 新增了不少的框架,这篇博客专注于研究 TipKit

Important

In macOS 14 Beta 4, TipKit projects don’t build in Simulator or for macOS due to macro “could not be found” errors. A workaround for this issue is available in Xcode release notes.

首先,引包: import TipKit

提示内容

定义提示内容,包含:标题(必须)、描述、图标、按钮等…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct TappableTip: Tip {
var title = Text("Tappable")
// var message = Text("Tap to see more info.") <- don't use this.
var message: Text? {
Text("Tap to see more info.")
}
// var asset = Image(systemName: "hand.tap") <- don't use this.
var asset: Image? {
Image(systemName: "hand.tap")
}
var actions: [Action] {
Action(perform: {
print("action 1")
}){
Text("Action 1")
}

Action(perform: {
print("action 2")
}){
Text("Action 2")
}
}
}

这里需要注意的是,message & asset 类型一定要手动写成 Optional,否则可能无法正确显示。

显示你的第一条提示

在 SwiftUI 中,有两种方式:

  • TipView(TappableTip()): Banner 样式
  • .popoverTip(TappableTip()):Popover 弹窗,与传统 Popover 不同的是需要手动点击关闭按钮才会触发 dismiss (macOS)

当你加了这些之后,你会发现,啥都没有…

这是因为 TipView 需要先读取你的设置:

1
2
RootView()
.task { try? await Tips.configure() }

static func configure(@ConfigurationBuilder options: @escaping () -> some TipsConfiguration = { defaultConfiguration }) async throws

Called at app startup to load and configure the persistent state of all tips in your app.

对于 UIKit 和 AppKit,也有对应的TipUIViewTipNSView,具体可参考官方文档说明

配置

刚才也提到了在 App 周期内需要调用 Tips.configure() 来配置 TipView,其中有这些可配置的选项:

  • 频率限制:DisplayFrequency,在一个周期内(小时,天,星期,月…)最多显示一条提示,在 defaultConfiguration 中,该值为 .immediate,即不限制显示条数。
  • 配置存储位置:DatastoreLocation,在 defaultConfiguration 中的位置为沙盒路径下的 Application Support

目前(Xcode 15 beta 5)貌似还有些问题,设置频率限制但仍然显示多个 Tips。

自定义显示规则

在用户可能需要该功能时显示提示会极大程度的帮助用户更好的上手一款 App,因此,我们可能会制定很多Tips,但是不需要一次性全都显示出来,有交互性的显示提示是有必要的。

例如:

  1. TODO App 中提示用户“如何标记某一项任务已完成”就应该在用户创建第一个任务时显示。
  2. TODO App 中提示用户“如何删除已完成的 TODO Item 记录”应该在用户标记完成了一些任务后显示。

Parameter

持久化参数。

我粗略地将其理解为 AppStorage,设定的参数会被持久化地存储在配置中定义的位置下。

参数的存在,为自定义显示的逻辑起到了很大的帮助。

此时,我们可以定义一个参数来查看 TODO List 中是否包含一个任务:

1
2
3
4
struct Tasks {
@Parameter
static var hasTasks: Bool = false
}

Xcode 15 beta 5 提示 Macro 错误,与官方提示的已知问题一致。

Event

可能会发生一次或多次的事件,可以简单理解为「进阶版的 Parameter」

定义 Event:

1
2
3
4
5
6
struct TodoApp: App {
static let taskDidComplete = Tips.Event(id: "taskDidComplete")
var body: some Scene {

}
}

用户完成时触发事件:

1
2
3
Button(“Complete”) {
TodoApp.taskDidComplete.donate()
}

在完成 donate() 之后,会持久化触发时间,并让计数器➕1,后续可以根据事件在某一时间段内的 donations 来制定规则。

更高级的玩法是,可以 Codable & Sendable 的数据关联给 Donation,后续也可以在事件中根据关联类型进行更加精准地筛选。

Rule

自定义 Tip 的显示规则。

  1. 根据参数中的 hasTasks 来决定是否显示“标记完成”的提示。
1
2
3
4
5
6
7
8
9
struct CompleteTaskTip: Tip {

var rules: [Rule] {
// Tip will only display when `hasTasks` is true.
#Rule(Tasks.hasTasks) {
$0 == true
}
}
}
  1. 根据事件收集到的 donations 决定是否显示“删除”提示。
1
2
3
4
5
6
7
8
9
struct DeleteTasksTip: Tip {
...
var rules: [Rule] {
#Rule(TodoApp.taskDidComplete) {
// Displays delete tip if user completes more than 3 tasks within an hour.
$0.donations.donatedWithin(.hours(1)).count > 3
}
}
}

当然,如果此时达到了配置中预设的频率限制,也不会显示。

样式

TipKit 中包含了 TipViewStyle 以允许开发者自己定义样式,但是当我定义好样式准备使用时,发现并没有提供修改样式的 modifier,类似 .tipViewStyle(.customStyle) 这样的 😡

目前内置提供了一种 MiniTipViewStyle,应该后续会有 .tipViewStyle 吧…

手动触发

显示提示

1
2
3
4
5
Tips.showAllTips() // Show All Tips

Tips.showTips([DeleteTasksTip.self, CompleteTaskTip.self]) // Show Tips based on Type

Tips.showTips([DeleteTasksTip().id]) // Show Tips based on ID

隐藏提示

1
2
3
4
5
Tips.hideAllTips() // Hide All Tips

Tips.hideTips([DeleteTasksTip.self, CompleteTaskTip.self]) // Hide Tips based on Type

Tips.hideTips([DeleteTasksTip().id]) // Hide Tips based on ID
作者

LiYanan

发布于

2023-07-27

更新于

2023-07-27

许可协议

评论