SwiftUI Focus 烹饪指南:焦点控制、键盘导航与自定义交互
The SwiftUI cookbook for focus
2023年6月5日
一句话判断
iOS 17 的 SwiftUI Focus API 支持 edit 和 activate 两种交互类型、FocusedValue 数据流和 Focus Section 方向控制——键盘导航和 tvOS 遥控器交互从此有了完整的工具链。
这场 Session 讲了什么
Cody 从 SwiftUI 团队出发,用三道菜的比喻讲解了 Focus 系统:
Focus 的本质。当用户按键盘按键、Apple TV 遥控器滑动或 Apple Watch 数码表冠旋转时,系统需要知道输入的目标。鼠标/触摸有坐标定位,但键盘和遥控器没有。Focus 就是”注意力光标”——告诉系统哪个视图应该接收输入。
Focusable 的两种交互类型(iOS 17 新增)。.focusable(.edit) 用于需要持续焦点输入的控件(如文本框),.focusable(.activate) 用于将焦点作为点击替代的控件(如按钮)。在 macOS Sonoma 之前,focusable 只支持 activate 语义,升级后需要验证行为是否符合预期。
FocusState。用 @FocusState 绑定观察焦点位置。支持 Bool 类型(单个视图)和自定义数据类型(多视图管理)。可以编程式改变焦点。
Focused Values。解决远程 UI 部分之间的数据依赖问题。定义 FocusedValueKey,通过 .focusedValue() 提供数据,在菜单命令等远程位置通过 @FocusedBinding 读取。随焦点移动自动更新。
Focus Sections。影响 Tab 键和遥控器方向键的焦点移动路径。可以定义焦点在一个区域内部循环,而不是跳到其他区域。
值得深挖的点
Focused Values 的数据流模式。它和 Environment Values 类似,但作用域是”焦点所在的视图层次”而非”视图树的父到子”。菜单命令可以读取当前焦点视图提供的数据,不需要全局状态或回调。
macOS 的键盘导航设置。macOS 默认不把焦点给按钮——用户需要在系统设置的”键盘导航”开关打开后,Tab 键才能聚焦按钮。这意味着大部分 macOS 用户不会看到按钮的焦点环,但你的代码需要正确处理两种状态。
tvOS 的方向性焦点移动。Apple TV 遥控器支持上下左右滑动,焦点在控件的二维网格中移动。Focus Sections 让你可以把相关的控件分组,遥控器在组内导航更自然。
代码片段
// 自定义控件的两种焦点交互类型
struct CustomSlider: View {
@FocusState private var isFocused: Bool
var body: some View {
RoundedRectangle(cornerRadius: 8)
.fill(isFocused ? .blue : .gray)
// 编辑型焦点:持续输入(如调整滑块值)
.focusable(.interactions.edit) {
// 获得焦点时的回调
} onUnfocus: {
// 失去焦点时的回调
}
.focused($isFocused)
}
}
// Focused Values:连接焦点视图和菜单命令
// 1. 定义 Key
struct SelectedTextKey: FocusedValueKey {
typealias Value = Binding<String>
}
// 2. 扩展 FocusedValues
extension FocusedValues {
var selectedText: Binding<String>? {
get { self[SelectedTextKey.self] }
set { self[SelectedTextKey.self] = newValue }
}
}
// 3. 在视图中提供值
@State var text = ""
.focusedValue(\.selectedText, $text)
// 4. 在菜单命令中读取
struct TextCommands: Commands {
@FocusedBinding(\.selectedText) var selectedText
var body: some Commands {
CommandMenu("编辑") {
Button("转为大写") {
selectedText?.wrappedValue = selectedText?.wrappedValue.uppercased() ?? ""
}
}
}
}
最佳实践
- 自定义控件使用
.focusable(.interactions.edit)或.activate精确指定交互类型。 - macOS 上的旧代码升级到 Sonoma 后要验证 focusable 的行为变化。
- Focused Values 适合菜单命令、工具栏按钮等需要感知焦点上下文的 UI。
- Focus Sections 让复杂布局中的键盘导航更可预测。
- 测试时要覆盖键盘导航开启和关闭两种状态(macOS)。
还有什么值得关注
- macOS 的焦点环、watchOS 的绿色边框、tvOS 的悬浮效果是各平台焦点视觉的体现
- Focus Section 对 Apple TV 遥控器的方向导航尤其重要
- Focused Values 和 Environment Values 的设计模式非常相似,容易迁移
- 这个 Session 是”cookbook”风格,适合按需查阅特定场景