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

2023-09-11 10:39:07

在这里插入图片描述

概览

作为我们秃头开发者来说,写出一款创意炸裂的 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)
    }
}

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

struct FavoriteTip: Tip {
    var image: Image? {
        Image(systemName: "heart")
    }
}

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)
                }
            }
        }
    }
}

在这里插入图片描述

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

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 中已不能使用。

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 提示才能显示出来:
在这里插入图片描述

为 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 的使用“秘技”了:

在这里插入图片描述

如何测试 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 中的各种特性和使用指南,小伙伴们还不快操练起来!

感谢观赏,再会!😎

更多推荐

警惕!多本SCI/SSCI被剔除,9月SCI/SSCI期刊目录已更新~(附下载)

【SciencePub学术】2023年9月20日,科睿唯安更新了WebofScience核心期刊目录。继上次SCI期刊目录和SSCI期刊目录更新之后,本次9月更新共有9本期刊发生变动:•SCIE:有3本期刊不再被SCIE期刊目录收录(EditorialDe-listing/ProductionDe-listing),1

避雷!这9本期刊已被剔除!9月SCI/SSCI目录已更新(附2023年WOS历次更新全目录)

2023年9月20日,科睿唯安更新了WebofScience核心期刊目录。此次更新后SCIE期刊目录共包含9490本期刊,SSCI期刊目录共包含3552本期刊。此次SCIE&SSCI期刊目录更新,与上次更新(2023年8月)相比,共有9本期刊发生变动。其中有3本SCIE期刊因不满足收录的质量标准或未从出版社接收到相关内

基于Java+SpringBoot+Vue+echarts校园资料分享平台设计和实现

博主介绍:✌全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌🍅文末获取源码联系🍅👇🏻精彩专栏推荐订阅👇🏻不然下次找不到哟2022-2024年最全的计算机软件毕业设计选题

应用开发平台能力扩展——集成echarts组件实现图表展现能力

背景图表展现能力是平台需具备的基础能力,目前echarts是最佳选择。在早期版本的图表中,不同的图表样式,需要不同的数据格式,需要进行复杂封装才能易于使用。百度官方也意识到这个问题,在echarts4.0版本提供了dataset属性支持,提供了统一的数据格式,也曾考虑基于这一新特性将常用图表进行封装。后来,发现了饿了么

基于Java+SpringBoot+Vue+echarts健身房管理系统设计和实现

博主介绍:✌全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌🍅文末获取源码联系🍅👇🏻精彩专栏推荐订阅👇🏻不然下次找不到哟2022-2024年最全的计算机软件毕业设计选题

autosar 诊断入门

AUTOSAR(汽车开放系统架构)是一个国际汽车行业的开放和标准化的软件架构。它的主要目标是为了创建一种独立于硬件的软件架构,以提高汽车电子系统的模块化和可重用性。AUTOSAR架构主要分为两个部分:AUTOSARRuntimeEnvironment(RTE)和AUTOSARSoftwareComponents(SWC

嵌入式学习笔记(31)异常向量表的编程处理

6.5.1像内存一样去访问异常向量表(1)S5PV210的异常向量表可以改变(在CP15协处理器中),以适应操作系统的需求。但是目前系统刚启动,此时DRAM尚未初始化,程序哦都市在iSRAM中运行。210在iSRAM中设置了异常向量表,供暂时性使用。(2)查210的iROMapplicationnote文档中iRAM的

线性代数基础-行列式

一、行列式之前的概念1.全排列:把n个不同的元素排成一列,称为n个元素的全排列,简称排列(实际上就是我们所说的排列组合,符号是A,arrange)2.标准序列:前一项均小于后一项的序列就是标准序列比如1,3,6,7,9就是标准序列3.逆序数:序列中满足前一项大于后一项的数对个数比如有一个序列:{1,6,9,2,3,4}

[Qt]多线程和套接字通信

文章目录1.多线程的使用1.1线程类QThread1.1.1常用共用成员函数1.1.2信号槽1.1.3静态函数1.1.4任务处理函数1.2使用方式11.2.1操作步骤1.2.2示例代码1.3使用方式21.3.1操作步骤1.3.2示例代码2.线程池的使用2.1QRunnable2.2QThreadPool3.套接字通信3

Unity Shader顶点数据疑问

1)UnityShader顶点数据疑问2)Unity2018发布在iOS16.3偶尔出现画面不动的问题3)安卓游戏启动后提示“应用程序异常”这是第352篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地掌握和学习。RenderingQ:把下面这段Shader

C++ PrimerPlus 复习 第七章 函数——C++的编程模块(下)

第一章命令编译链接文件make文件第二章进入c++第三章处理数据第四章复合类型(上)第四章复合类型(下)第五章循环和关系表达式第六章分支语句和逻辑运算符第七章函数——C++的编程模块(上)第七章函数——C++的编程模块(下)本章重要点注意函数指针,const指针参数。其他的其实都简简单单第七章函数——C++的编程模块(

热文推荐