概览
作为我们秃头开发者来说,写出一款创意炸裂的 App 还不足以吸引用户眼球,更重要的是如何让用户用最短的时间掌握我们 App 的使用技巧。
从 iOS 17 开始, 推出了全新的 TipKit 框架专注于此事。有了它,我们再也不用自己写 App 用户帮助以及使用指南的逻辑和界面了。
使用 TipKit 非常简单,接下来就让我们一起走进 TipKit 的世界吧!
文章目录
本文代码全部在 Xcode 15 beta8 上编译,在 iOS 17 beta8 上运行。
博文对应的视频课在此,请小伙伴们恣意观看
SwiftUI 5.0 TipKit 让用户更懂你的 App
1. 什么是 TipKit?
TipKit 是 在 WWDC 23 上推出的一款新框架,用于在界面显示提示(Tips)来帮助用户快速发掘我们 App 的使用特性。
目前该框架仍属于 beta 阶段,意味着它还有很多不确定性。
如果我没有记错, 直到 Xcode beta4 才将 TipKit 提供给开发者,而且现在 官网 TipKit 的示例代码在 Xcode beta8 中已提示语法错误了(我们后面会说明):
对于 这种习惯性“谜之”行为的更多细节,感兴趣的小伙伴们可以到如下链接中观赏:
2. 创建一个 Tip
按照 SwiftUI 的“习性”,一个 Tip 同时意味着外观和逻辑双重含义。
创建一个提示很简单,只需遵循 Tip 协议即可:
struct FavoriteTip: Tip {
var title: Text {
Text("收藏最爱的图片")
.bold()
}
var message: Text? {
Text("将心仪的图片保存到相册中")
.font(.headline)
.foregroundStyle(.gray.gradient)
}
}
Tip 协议还有很多其它可选属性,比如我们还可以为 Tip 界面进一步增加图片修饰:
struct FavoriteTip: Tip {
var image: Image? {
Image(systemName: "heart")
}
}
3. TipKit 显示的两种方式
在 Tip 创建之后如何显示它们呢?有两种方式:嵌入和弹出。
我们可以直接将 Tip 嵌在视图中:
struct ContentView: View {
let favTip = FavoriteTip()
var body: some View {
NavigationStack {
VStack {
TipView(favTip)
}
.padding()
.navigationTitle("TitKit演示")
}
}
}
显示效果如下:
或者我们还可以将 Tip 直接依附于某一个视图,比如图片或按钮:
struct ContentView: View {
let favTip = FavoriteTip()
var body: some View {
NavigationStack {
VStack {...}
.padding()
.navigationTitle("TitKit演示")
.toolbar {
ToolbarItem {
Image(systemName: "heart")
.font(.title.weight(.black))
.foregroundStyle(.pink.gradient)
.popoverTip(favTip, arrowEdge: .top)
}
}
}
}
}
我们可以根据不同需求来组合使用这两种显示方式。
更多定制 Tip 外观的方法,请小伙伴们移步下面的博文观赏:
4. TipKit 全局配置
其实,TipKit 框架会在 App (本地目录)中存放一些相关的配置信息。理论上说,它们可能会通过 iCloud 同步到其它设备上去,这意味着在不同设备上相同 App 中的 TipKit 共享同一组配置。
我们可以进一步定制 TipKit 的配置细节,比如 Tip 显示频率、配置数据库保存的本地位置等等:
import SwiftUI
import TipKit
@main
struct TipKitTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.task {
try? Tips.configure([
.displayFrequency(.immediate),
.datastoreLocation(.applicationDefault)
])
// Xcode 15 beta4 中过时的语法,目前已不能使用:
/*
try? await Tips.configure {
DisplayFrequency(.immediate)
DatastoreLocation(.applicationDefault)
}*/
}
}
}
}
如上代码所示,我们需要所有 Tip 立即显示,并且让系统决定配置数据存储的位置(我们也可以自己设置存储路径)。
在代码中,我们注释了之前官方示例中出错的代码片段,这些代码在 Xcode 15 beta8 中已不能使用。
5. TipKit 显示规则
除了全局 Tip 显示限制以外,我还可以设置单个 Tip 之间的显示规则。
比如,假设有两个提示,我们希望 FavoriteTip 在 StartTip 提示关闭后再显示,我们可以在 FavoriteTip 中用特定的规则(Rule)来表示这一约束:
struct FavoriteTip: Tip {
// 其它代码从略
var rules: [Rule] {
#Rule(Self.$startTipHasDisplayed) { $0 == true}
}
@Parameter
static var startTipHasDisplayed: Bool = false
}
现在,我们需要在 StartTip 提示关闭时将 FavoriteTip.startTipHasDisplayed 置为 true 才能触发 FavoriteTip 的显示:
struct ContentView: View {
let startTip = StartTip()
let favTip = FavoriteTip()
var body: some View {
NavigationStack {
VStack {
Image("1")
.resizable()
.aspectRatio(contentMode: .fill)
.onTapGesture {
// 关闭 startTip 提示
startTip.invalidate(reason: .actionPerformed)
// 触发 FavoriteTip 提升的显示
FavoriteTip.startTipHasDisplayed = true
}
TipView(startTip)
}
.padding()
.navigationTitle("TitKit演示")
.toolbar {
ToolbarItem {
Image(systemName: "heart")
.font(.title.weight(.black))
.foregroundStyle(.pink.gradient)
.popoverTip(favTip, arrowEdge: .top)
}
}
}
}
}
现在,只有等 StartTip 关闭后,FavoriteTip 提示才能显示出来:
6. 为 TipKit 增加更多互动性
有时,我们希望提示为用户提供更丰富的交互功能,比如在 Tip 中提供按钮跳转到更详细的使用教程界面。
TipKit 为此也提供了很好的支持,我们可以为 Tip 添加 Action 来驱动交互行为:
struct FavoriteTip: Tip {
// 其它代码从略
var actions: [Action] {
[
Tip.Action(id: "learn-more", title: "了解更多"),
Tip.Action(id: "forget", title: "下次再说")
]
}
}
在 Tip Action 被触发时,我们可以执行自定义行为:
struct ContentView: View {
let startTip = StartTip()
let favTip = FavoriteTip()
var body: some View {
NavigationStack {
VStack {
Image("1")
.resizable()
.aspectRatio(contentMode: .fill)
.onTapGesture {
startTip.invalidate(reason: .actionPerformed)
FavoriteTip.startTipHasDisplayed = true
}
TipView(startTip)
}
.padding()
.navigationTitle("TitKit演示")
.toolbar {
ToolbarItem {
Image(systemName: "heart")
.font(.title.weight(.black))
.foregroundStyle(.pink.gradient)
.popoverTip(favTip, arrowEdge: .top){ action in
switch action.index {
case 0:
favTip.invalidate(reason: .tipClosed)
// 跳转到详细使用教程
case 1:
favTip.invalidate(reason: .tipClosed)
// 直接退出提示
default:
break
}
}
}
}
}
}
}
现在,用户可以选择“了解更多”来进一步学习 App 的使用“秘技”了:
7. 如何测试 TipKit?
TipKit 全局配置存储在本地带有持久化特性,为了便于开发者即时测试, 提供了一些方法来快速显示或隐藏全部或指定 Tip:
一般的,要想在 Xcode 预览中正确测试 TipKit 的行为,我们需要在每次视图刷新时重置 TipKit 数据库,否则 Tip 不会正常显示:
#Preview {
ContentView()
.task {
// 在每次视图刷新时将 TipKit 数据库重置为初始状态
try? Tips.resetDatastore()
try? Tips.configure([
.displayFrequency(.immediate),
.datastoreLocation(.applicationDefault)
])
}
}
总结
在本篇博文中,我们介绍了 SwiftUI 5.0(iOS 17)中新引进的开发框架 TipKit,使用它我们可以非常方便和快速的向用户介绍我们 App 中的各种特性和使用指南,小伙伴们还不快操练起来!
感谢观赏,再会!😎