Highlight
Apple 用 Toasty 示例给出可发现设计的五个方法:排列功能优先级、提供视觉暗示、提示手势、按用户行为组织内容,并让用户控制个性化推荐。
核心内容
很多 App 都有一个相同的问题:功能已经做完了,用户却不知道它们存在。
最常见的补救办法是 onboarding(新手引导)。打开 App,先让用户看几屏说明。Toast 应用 Toasty 也遇到这个问题。它既能浏览吐司食谱,也能查看朋友做的吐司,还能拍照生成食谱。
如果把这些功能一条一条塞进引导页,用户很可能直接跳过。即使用户读完,也不一定能在几分钟后记住。
Apple 在这场 session 中给出的方向是:界面要自己说明自己。用户看到屏幕时,应该能在动手前知道能做什么,以及怎么做。
从功能列表回到用户目标
Toasty 的功能很多。浏览食谱、查看我的食谱、拍照生成食谱、搜索、设置、关于页面,都可以说重要。
如果全部放到首页,界面会拥挤。如果全部收进汉堡菜单,用户又看不到入口。
Apple 的做法是先按用户目标排序。浏览食谱和查看我的食谱最常用,放进底部 tab bar(标签栏)。拍照生成食谱重要,但属于“我的食谱”里的动作,所以放在该页面顶部。设置和个人偏好改动不频繁,可以放在 profile(个人资料)图标后面。
这里的判断标准很具体:功能是否属于 App 的核心,用户是否经常使用,入口位置是否符合手指够得到的范围。
用界面暗示替代引导页
可发现设计不要求删掉所有说明文字。
它要求说明出现在用户需要的地方。图标不清楚,就加文字标签。搜索框让人不知道输入什么,就给出“按食材、地点、朋友搜索”的提示。拍照功能需要用户把吐司放到合适位置,就在相机画面里显示轮廓、禁用快门按钮,并在角度合适时给动画和触觉反馈。
这比提前在引导页里讲一遍更直接。用户正在操作,相同的信息才有用。
个性化推荐也要可控制
Toasty 还有机器学习推荐。它会根据用户喜欢的食谱,生成 Tasty Picks(个性化推荐)。
推荐系统的问题是来源不透明。用户看到一组吐司,不知道这是编辑精选,还是机器学习结果,也不知道如何纠正它。
Apple 的做法是把原因写出来,例如“Because you added avocado toast”。用户知道推荐来自自己添加过的牛油果吐司。再提供“Edit suggestion”按钮,让用户选择“Suggest Less Avocado Toast”或“Stop Suggesting Avocado Toast”。
推荐需要透明。用户能看懂来源,也能修改结果,才会继续信任它。
详细内容
这场 session 没有提供代码片段。它是一场设计方法演讲。下面用可执行的 SwiftUI 原型,把 transcript 中的关键方法落到界面结构上。示例只使用系统控件和文案,不引入演讲中没有出现的自定义 API。
1. 按重要性排列功能
(03:18)Apple 的第一条原则是 prioritize important features(优先展示重要功能)。核心功能要立刻可见,非核心功能可以多一次导航。
Toasty 没有把所有功能塞进一个屏幕,也没有放进汉堡菜单。它把“推荐”和“我的食谱”放进 tab bar,把“新建食谱”放在我的食谱页面顶部,把个人偏好放进 profile 图标。
import SwiftUI
struct ToastyAppView: View {
var body: some View {
TabView {
ForYouView()
.tabItem {
Label("For You", systemImage: "sparkles")
}
RecipesView()
.tabItem {
Label("Recipes", systemImage: "book")
}
}
}
}
struct ForYouView: View {
var body: some View {
NavigationView {
List {
Section("Tasty Picks") {
Text("Restaurant toast near you")
Text("Toast shared by friends")
}
}
.navigationTitle("For You")
.toolbar {
Button {
showProfile()
} label: {
Image(systemName: "person.crop.circle")
}
}
}
}
}
struct RecipesView: View {
var body: some View {
NavigationView {
List {
Button {
captureToast()
} label: {
Label("New", systemImage: "camera")
}
Section("My Recipes") {
Text("Avocado toast")
Text("Blueberry toast")
}
}
.navigationTitle("Recipes")
.searchable(text: .constant(""), prompt: "Search ingredients, places, or friends")
}
}
}
func showProfile() {}
func captureToast() {}
关键点:
TabView把最重要、最高频的两个页面放到底部标签栏,用户打开 App 就能看到。Label("For You", systemImage: "sparkles")同时提供文字和图标,避免图标含义不清。toolbar中的 profile 按钮承载个人偏好,适合不频繁修改的内容。Button放在RecipesView的列表顶部,让“拍照生成食谱”在相关页面内保持醒目。.searchable的prompt给出输入例子,解决搜索框空白时不知道搜什么的问题。
2. 用文字、图标和上下文提供视觉暗示
(08:12)第二条原则是 visual cues(视觉暗示)。视觉暗示的作用是告诉用户某个元素是什么、能做什么、何时该做。
Apple 在 Toasty 中提到三个例子。底部标签栏的图标需要文字标签。相机按钮只用相机图标不够,因为相机可能代表拍照、扫描文档或扫描二维码。搜索框需要提示用户可以按食材、地点或朋友搜索。
import SwiftUI
struct DiscoverableSearchView: View {
@State private var query = ""
var body: some View {
NavigationView {
List {
Section("Try searching") {
Text("Ingredients")
Text("Places")
Text("Friends")
}
Section("Actions") {
Button(action: captureToast) {
Label("New", systemImage: "camera")
}
}
}
.navigationTitle("Toasty")
.searchable(
text: $query,
prompt: "Search ingredients, places, or friends"
)
}
}
}
func captureToast() {}
关键点:
@State private var query保存搜索输入,示例可以直接在 SwiftUI 中运行。Section("Try searching")在界面里给出可搜索内容类型,帮助用户开始输入。Label("New", systemImage: "camera")用“New”说明相机按钮的结果是新建食谱。.searchable的prompt把 transcript 中“ingredients, places, or friends”的提示放进系统搜索框。NavigationView和List使用 iOS 常见结构,降低用户学习成本。
3. 把手势当快捷方式,并用动画提示
(12:59)第三条原则是 hint at gestures(提示手势)。手势能让导航更流畅,但手势本身不可见。
Apple 的建议很直接:优先使用平台标准手势;自定义手势要贴近真实世界;手势只做快捷方式,不要替代可见按钮。
Toasty 的食谱详情页支持向下滑动返回列表,同时保留明确的返回按钮。点击返回按钮时,动画方向也向下,暗示用户可以下滑。
import SwiftUI
struct RecipeDetailView: View {
@Environment(\.dismiss) private var dismiss
@State private var offsetY: CGFloat = 0
var body: some View {
VStack(spacing: 24) {
Button("Back to recipes") {
withAnimation(.easeInOut) {
offsetY = 700
}
dismiss()
}
Text("Avocado toast")
.font(.largeTitle)
Text("Swipe down as a shortcut to go back.")
.foregroundStyle(.secondary)
}
.offset(y: offsetY)
.gesture(
DragGesture()
.onEnded { value in
if value.translation.height > 120 {
dismiss()
}
}
)
}
}
关键点:
@Environment(\.dismiss)使用系统提供的关闭能力,保留明确的返回路径。Button("Back to recipes")是主要操作入口,用户不需要先发现手势。withAnimation(.easeInOut)让点击按钮后的运动方向与下滑手势一致。.offset(y: offsetY)让视图向下移动,匹配“swipe down to go back”的方向。DragGesture只在用户下滑超过阈值时触发dismiss(),手势是快捷方式。
4. 按用户行为组织大量内容
(17:37)第四条原则是 organize by behavior(按行为组织)。当内容很多时,分类不能只看数据字段,还要看用户在真实生活中怎样发现内容。
Toasty 起初按食材分类,例如牛油果、蓝莓。但食材组合太多,用户会被分类淹没。后来改成餐厅推荐、朋友分享和 Tasty Picks。前两类来自现实行为:试新餐厅、听朋友推荐。第三类来自个性化推荐。
import SwiftUI
struct ToastCategory: Identifiable {
let id = UUID()
let title: String
let items: [String]
}
struct BehaviorBasedContentView: View {
let categories = [
ToastCategory(
title: "From restaurants",
items: ["Cafe avocado toast", "Market tomato toast"]
),
ToastCategory(
title: "Shared by friends",
items: ["Mylène's blueberry toast", "Jiabao's banana toast"]
),
ToastCategory(
title: "Tasty Picks",
items: ["Suggested sesame toast", "Suggested honey toast"]
)
]
var body: some View {
List(categories) { category in
Section(category.title) {
ForEach(category.items, id: \.self) { item in
Text(item)
}
}
}
.navigationTitle("For You")
}
}
关键点:
ToastCategory表示一个可见分类,分类名来自用户行为场景。From restaurants对应 transcript 中用户通过餐厅发现新吐司。Shared by friends对应朋友推荐的发现路径。Tasty Picks对应机器学习推荐生成的个性化内容。Section(category.title)用标题把内容分组,让用户一眼看到分类边界。
5. 让用户理解并控制推荐系统
(22:35)第五条原则是 convey a sense of control(传达控制感)。用户要知道推荐来自哪里,也要能修改不想要的推荐。
Apple 区分了 explicit feedback(显式反馈)和 implicit feedback(隐式反馈)。显式反馈是用户主动告诉系统想多看或少看什么。隐式反馈来自用户行为,例如查看食谱、分享吐司或添加食谱。
Toasty 最终用更明确的文案说明后果:“Suggest toast like this”“Because you added avocado toast”“Edit suggestion”。
import SwiftUI
struct RecommendationControlView: View {
@State private var showingEditOptions = false
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Tasty Picks")
.font(.title)
HStack {
Text("Because you added avocado toast")
.foregroundStyle(.secondary)
Button("Edit suggestion") {
showingEditOptions = true
}
}
Text("Banana avocado toast")
.font(.headline)
HStack {
Button("Suggest toast like this") {
suggestMore()
}
Button("Less") {
suggestLess()
}
}
}
.padding()
.confirmationDialog("Edit suggestion", isPresented: $showingEditOptions) {
Button("Suggest Less Avocado Toast") {
suggestLessAvocadoToast()
}
Button("Stop Suggesting Avocado Toast", role: .destructive) {
stopSuggestingAvocadoToast()
}
}
}
}
func suggestMore() {}
func suggestLess() {}
func suggestLessAvocadoToast() {}
func stopSuggestingAvocadoToast() {}
关键点:
Text("Because you added avocado toast")披露推荐的输入来源,解释为什么看到这些内容。Button("Edit suggestion")直接写出用户能编辑推荐,不藏在只有图标的更多菜单里。Button("Suggest toast like this")用动词说明点击后的后果是影响推荐。Button("Less")对应减少相似内容的显式反馈。confirmationDialog展示两个具体选项,用户能选择少推荐或停止推荐牛油果吐司。
核心启发
-
做什么:给笔记 App 增加“按最近行为推荐模板”的首页区块。 为什么值得做:session 提到个性化内容适合大量内容场景,但必须披露来源。 怎么开始:用最近创建的笔记类型生成推荐,并在区块标题下写“因为你最近创建了会议笔记”,旁边放“编辑建议”按钮。
-
做什么:给图片编辑 App 的手势操作增加可见替代入口。 为什么值得做:Apple 建议手势作为快捷方式,不能替代主要操作。 怎么开始:保留双指缩放和滑动切换滤镜,同时在工具栏提供“缩放”“上一个”“下一个”按钮,并让动画方向与手势一致。
-
做什么:给搜索页增加可输入内容示例。 为什么值得做:搜索框容易出现空白页问题,用户不知道该搜什么。 怎么开始:在
.searchable的 prompt 中写出可搜索维度,例如“搜索项目、成员或标签”,并在空状态展示三个示例查询。 -
做什么:给内容型 App 重新设计分类,不按数据库字段分组。 为什么值得做:session 中 Toasty 从食材分类改为餐厅、朋友和推荐,分类贴近发现行为。 怎么开始:访谈用户如何找到内容,把“来源”“场景”“关系”作为一级分组,再用
Section或卡片标题强化边界。 -
做什么:给推荐流加入“为什么看到”和“减少此类内容”。 为什么值得做:用户能理解隐式反馈来源,也能纠正推荐结果。 怎么开始:在推荐卡片下显示原因,例如“因为你收藏了 X”,再提供“减少类似内容”“停止推荐 X”两个操作。
关联 Session
- The practice of inclusive design — 从语言、图像和界面内容入手,减少用户理解和使用 App 的障碍。
- Design great actions for Shortcuts, Siri, and Suggestions — 讲如何把功能设计成用户能理解、能组合、能自动化的 action。
- Design for Safari 15 — 讲 Safari 15 新界面下的网站和 Web App 设计约束。
- Explore the latest updates to SF Symbols — 图标含义和视觉层级是可发现设计的一部分,这场 session 补充了系统图标的设计方法。
评论
GitHub Issues · utterances