• SwiftUI 5.0(iOS 17)TipKit 让用户更懂你的 App


    在这里插入图片描述

    概览

    作为我们秃头开发者来说,写出一款创意炸裂的 App 还不足以吸引用户眼球,更重要的是如何让用户用最短的时间掌握我们 App 的使用技巧。

    在这里插入图片描述

    iOS 17 开始, 推出了全新的 TipKit 框架专注于此事。有了它,我们再也不用自己写 App 用户帮助以及使用指南的逻辑和界面了。

    使用 TipKit 非常简单,接下来就让我们一起走进 TipKit 的世界吧!

    本文代码全部在 Xcode 15 beta8 上编译,在 iOS 17 beta8 上运行。


    什么是 TipKit?

    在这里插入图片描述

    TipKit 是  在 WWDC 23 上推出的一款新框架,用于在界面显示提示(Tips)来帮助用户快速发掘我们 App 的使用特性。

    在这里插入图片描述
    在这里插入图片描述

    目前该框架仍属于 beta 阶段,意味着它还有很多不确定性。

    如果我没有记错, 直到 Xcode beta4 才将 TipKit 提供给开发者,而且现在 官网 TipKit 的示例代码在 Xcode beta8 中已提示语法错误了(我们后面会说明):

    在这里插入图片描述


    对于  这种习惯性“谜之”行为的更多细节,感兴趣的小伙伴们可以到如下链接中观赏:


    创建一个 Tip

    按照 SwiftUI 的“习性”,一个 Tip 同时意味着外观和逻辑双重含义。

    创建一个提示很简单,只需遵循 Tip 协议即可:

    struct FavoriteTip: Tip {
        var title: Text {
            Text("收藏最爱的图片")
                .bold()
        }
        
        var message: Text? {
            Text("将心仪的图片保存到相册中")
                .font(.headline)
                .foregroundStyle(.gray.gradient)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Tip 协议还有很多其它可选属性,比如我们还可以为 Tip 界面进一步增加图片修饰:

    struct FavoriteTip: Tip {
        var image: Image? {
            Image(systemName: "heart")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    TipKit 显示的两种方式

    在 Tip 创建之后如何显示它们呢?有两种方式:嵌入和弹出。

    我们可以直接将 Tip 嵌在视图中:

    struct ContentView: View {
        let favTip = FavoriteTip()
        
        var body: some View {
            NavigationStack {
                VStack {
                    
                    TipView(favTip)
                }
                .padding()
                .navigationTitle("TitKit演示")
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    显示效果如下:

    在这里插入图片描述

    或者我们还可以将 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)
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    我们可以根据不同需求来组合使用这两种显示方式。

    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)
                        }*/
                    }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    如上代码所示,我们需要所有 Tip 立即显示,并且让系统决定配置数据存储的位置(我们也可以自己设置存储路径)。

    在代码中,我们注释了之前官方示例中出错的代码片段,这些代码在 Xcode 15 beta8 中已不能使用。

    TipKit 显示规则

    除了全局 Tip 显示限制以外,我还可以设置单个 Tip 之间的显示规则。

    比如,假设有两个提示,我们希望 FavoriteTip 在 StartTip 提示关闭后再显示,我们可以在 FavoriteTip 中用特定的规则(Rule)来表示这一约束:

    struct FavoriteTip: Tip {
        // 其它代码从略
        
        var rules: [Rule] {
            #Rule(Self.$startTipHasDisplayed) { $0 == true}
        }
        
        @Parameter
        static var startTipHasDisplayed: Bool = false
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    现在,我们需要在 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)
                    }
                }
            }
        }
    }
    
    • 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
    • 35

    现在,只有等 StartTip 关闭后,FavoriteTip 提示才能显示出来:
    在这里插入图片描述

    为 TipKit 增加更多互动性

    有时,我们希望提示为用户提供更丰富的交互功能,比如在 Tip 中提供按钮跳转到更详细的使用教程界面。

    TipKit 为此也提供了很好的支持,我们可以为 Tip 添加 Action 来驱动交互行为:

    struct FavoriteTip: Tip {
        // 其它代码从略
        
        var actions: [Action] {
            [
                Tip.Action(id: "learn-more", title: "了解更多"),
                Tip.Action(id: "forget", title: "下次再说")
            ]
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在 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
                                }
                            }
                    }
                }
            }
        }
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    现在,用户可以选择“了解更多”来进一步学习 App 的使用“秘技”了:

    在这里插入图片描述

    如何测试 TipKit?

    TipKit 全局配置存储在本地带有持久化特性,为了便于开发者即时测试, 提供了一些方法来快速显示或隐藏全部或指定 Tip:

    在这里插入图片描述

    一般的,要想在 Xcode 预览中正确测试 TipKit 的行为,我们需要在每次视图刷新时重置 TipKit 数据库,否则 Tip 不会正常显示:

    #Preview {
        ContentView()
            .task {
                // 在每次视图刷新时将 TipKit 数据库重置为初始状态
                try? Tips.resetDatastore()
                
                try? Tips.configure([
                    .displayFrequency(.immediate),
                    .datastoreLocation(.applicationDefault)
                ])
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    总结

    在本篇博文中,我们介绍了 SwiftUI 5.0(iOS 17)中新引进的开发框架 TipKit,使用它我们可以非常方便和快速的向用户介绍我们 App 中的各种特性和使用指南,小伙伴们还不快操练起来!

    感谢观赏,再会!😎

  • 相关阅读:
    WRF-Chem在大气环境(PM2.5、臭氧)、能见度、城市化方面应用
    hyperf 十六 session
    PyQt5入门2——添加一个画布并且显示特定的图片
    用Postman测试需要授权的接口
    一个案例熟悉使用pytorch
    聚观早报 |OPPO Reno11系列官宣;荣耀100系列渲染图
    TCP socket && UDP && TCP协议 && IP协议 && 以太网等
    第2章 Java基础
    (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
    GET和POST的区别及使用场景?
  • 原文地址:https://blog.csdn.net/mydo/article/details/132796488