Principles of great design
Design 进阶 17m

出色设计的原则

Principles of great design

2026年6月10日

在 Apple 官方观看视频

一句话判断

这场 Session 最值得关注的一件事是:Apple 在 AI 时代对“责任(Responsibility)”划了硬红线——如果你的 AI 功能存在造成实质伤害的风险(比如给过敏用户推荐致命食材),直接砍掉这个功能,而不是甩锅给一句“AI 可能会出错”的免责声明。

这场 Session 讲了什么

很多开发者把 Design 理解为“把界面弄好看”或者“加几个炫酷动画”。Apple 这次直接把话挑明了:Design is making something with intention(设计是带着意图去创造)。每个你加上的 Feature 都在消耗用户的时间、注意力和信任。所以,决定“不做什么”往往比“做什么”更考验功底。

这次 Session 没有讲具体的 SwiftUI 新控件,而是把重点放在了 Agency(用户控制权)和 Responsibility(责任)上。特别是在 Responsibility 环节,Apple 拿 AI 功能举例,要求开发者必须预判模型幻觉带来的负面后果。这对那些急着把 LLM(大语言模型)塞进 App 里、却不管输出是否安全的团队来说,是一记响亮的警钟。

值得深挖的点

AI 时代的 Responsibility(责任)不是加个免责声明就行

Apple 在讲 Responsibility 时,专门提到了 AI 功能的安全护栏。很多 App 接入 AI 后,只在角落贴个“AI 生成内容可能不准确”的标签就完事了。Apple 的态度很明确:你要预判最坏的情况。比如一个食谱 App,如果用户设置了花生过敏,AI 却推荐了含有花生的菜谱,这不仅是体验问题,而是生命安全问题。

这里的 Trade-off 很明显:做安全护栏(比如增加人工审核、限制 AI 输出范围、增加二次确认)会增加开发成本和延迟。但 Apple 认为,如果风险大于价值,你应该直接移除这个 AI 功能,而不是让用户承担风险。在实战中,这意味着我们在设计 AI 交互时,不能只做“快乐路径(Happy Path)”的 UI,必须为“AI 犯蠢”设计兜底界面和阻断机制。

Agency(控制权)的落脚点是 Forgiveness(宽容度)

Session 里提到,给用户控制权(Agency)不是给他们一堆复杂的设置项,而是让他们敢于探索。怎么让人敢于探索?就是提供 Forgiveness(宽容度)。用户点错了、删错了,能轻松 Undo(撤销)。

很多开发者喜欢用各种 confirmationDialog(确认对话框)来防止用户误操作,但这其实打断了用户的心流。Apple 的建议是:让操作立即生效,但提供强大的 Undo 机制。只有在真正不可逆的破坏性操作(比如清空数据库、永久删除账号)时,才使用阻断式的确认弹窗。这种设计选择背后的逻辑是:信任用户的操作,把纠错的成本交给系统,而不是把防范的成本转嫁给用户。

代码片段

1. 用 Undo 代替烦人的确认弹窗

场景:删除列表项时,直接删除并提供 Undo 提示,而不是每次都弹 Alert 问“确定要删除吗?”。

struct TaskListView: View {
    @State private var tasks = ["写周报", "Review 代码", "摸鱼"]
    @Environment(\.undoManager) private var undoManager
    @State private var showUndoToast = false
    @State private var deletedTask: String?

    var body: some View {
        List {
            ForEach(tasks, id: \.self) { task in
                Text(task)
            }
            .onDelete { indexSet in
                // 记录被删除的任务和索引,用于撤销
                let index = indexSet.first!
                deletedTask = tasks.remove(at: index)
                
                // 注册 Undo 操作
                undoManager?.registerUndo(withTarget: self) { target in
                    if let task = target.deletedTask {
                        target.tasks.insert(task, at: index)
                    }
                }
                undoManager?.setActionName("删除任务")
                showUndoToast = true
            }
        }
        // 使用非阻断式的底部提示,代替 Alert
        .overlay(alignment: .bottom) {
            if showUndoToast {
                Text("已删除,点击撤销")
                    .padding()
                    .background(.ultraThinMaterial, in: Capsule())
                    .padding(.bottom, 40)
                    .onTapGesture {
                        undoManager?.undo()
                        showUndoToast = false
                    }
            }
        }
    }
}

坑:UndoManager 在 SwiftUI 中需要通过 @Environment 获取,如果在自定义的复杂视图层级(如跨 Window 或 Sheet)中丢失了 Environment,撤销操作会静默失败。

2. 负责任的权限请求时机

场景:不要在 App 启动时立刻请求位置或相册权限,而是等用户真正点击相关功能时再请求(Just-in-time)。

struct PhotoPickerView: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    @State private var showPicker = false

    var body: some View {
        VStack {
            Text("设置你的头像")
                .font(.headline)
            
            // 只有用户点击这里,才会触发系统权限弹窗
            Button("从相册选择") {
                showPicker = true
            }
            .buttonStyle(.borderedProminent)
        }
        // 使用 PhotosPicker 自动处理权限,且默认只请求选中的照片(Limited Access)
        .photosPicker(isPresented: $showPicker, 
                      selection: $selectedItems, 
                      matching: .images)
        .onChange(of: selectedItems) { _, newItems in
            // 处理选中的图片
        }
    }
}

坑:使用旧版的 UIImagePickerController 时,如果不小心请求了 Full Access(完全访问),会被 Apple 审核团队重点关照。尽量使用 SwiftUI 原生的 PhotosPicker,它默认采用最小权限原则。

3. 用触觉反馈增强 Craft(工艺感)

场景:在用户完成一个关键操作(如打卡、发送消息)时,提供细腻的触觉反馈,让界面更有“实体感”。

struct CheckInButton: View {
    @State private var isCheckedIn = false

    var body: some View {
        Button {
            isCheckedIn.toggle()
        } label: {
            Image(systemName: isCheckedIn ? "checkmark.circle.fill" : "circle")
                .font(.system(size: 50))
                .foregroundStyle(isCheckedIn ? .green : .gray)
                .animation(.spring(response: 0.3, dampingFraction: 0.6), value: isCheckedIn)
        }
        // 根据状态变化触发不同的触觉反馈
        .sensoryFeedback(.success, trigger: isCheckedIn) { oldValue, newValue in
            return newValue == true
        }
    }
}

坑:.sensoryFeedback 是 iOS 17 引入的,不要在 body 里直接调用 UIImpactFeedbackGenerator,那会破坏 SwiftUI 的声明式结构,且容易在视图重绘时重复触发。

最佳实践

对于现有项目,立刻排查 App 里的权限请求逻辑。把所有在 AppDelegateApp 初始化时直接弹出的权限请求,移到具体的业务触发点上。同时,审查所有带 AI 属性的功能,问自己一个问题:“如果 AI 输出了最离谱的结果,用户会受到实质性伤害吗?”如果会,加上强阻断或人工审核。

对于新项目,在设计交互时,把“撤销(Undo)”作为一等公民来考虑。能用 Undo 解决的误操作,就不要用 Alert 去打断用户。在 UI 层面,遵循 Familiarity(熟悉感),长得像按钮的东西就必须能点,长得一样的控件行为必须一致,不要为了所谓的“创新”去挑战用户的肌肉记忆。

还有什么值得关注

  • Simplicity(简洁)不是 Minimalism(极简):Apple 特意强调,简洁不是把界面元素删到最少,而是消除摩擦(removing friction)。有时候增加一些上下文提示,反而能让界面在认知上更“简洁”。
  • Delight(愉悦感)不是撒彩纸:不要靠满屏幕的 Confetti(彩纸动画)来制造愉悦。真正的愉悦感是前面所有原则(目的、控制权、工艺等)都做对之后,自然产生的结果。
  • Craft(工艺)需要长期主义:漂亮的排版、流畅的动画和稳定的性能是建立信任的基础。设计不是一次性的工作,随着平台和用户需求的变化,你需要持续迭代它。
Design-Principles AI-Responsibility SwiftUI-Undo PhotosPicker SensoryFeedback