focus-engine
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFocus Engine
焦点引擎
Focus behavior for SwiftUI and UIKit apps targeting iOS 26+, iPadOS, macOS, and tvOS. Covers keyboard focus, directional focus, scene-focused values, focus restoration, and UIKit focus guides. guidance in this skill applies to macOS and tvOS. Accessibility-specific focus for VoiceOver and Switch Control lives in the skill.
focusSection()ios-accessibility面向iOS 26+、iPadOS、macOS和tvOS的SwiftUI与UIKit应用的焦点行为。涵盖键盘焦点、方向焦点、场景焦点值、焦点恢复以及UIKit焦点引导。本技能中的指南适用于macOS和tvOS。VoiceOver与Switch Control的无障碍专属焦点相关内容请查看技能。
focusSection()ios-accessibilityContents
目录
SwiftUI FocusState
SwiftUI FocusState
Use to read and write focus placement inside a scene. Use for a single target or an optional enum for multiple targets.
@FocusStateBoolHashableswift
struct LoginView: View {
enum Field: Hashable { case email, password }
@State private var email = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
}
.onAppear { focusedField = .email }
.onSubmit {
switch focusedField {
case .email: focusedField = .password
case .password, nil: submit()
}
}
}
}Keep focus state local to the view that owns the focusable controls.
使用读取和写入场景内的焦点位置。单个目标使用类型,多个目标使用可选的枚举类型。
@FocusStateBoolHashableswift
struct LoginView: View {
enum Field: Hashable { case email, password }
@State private var email = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
}
.onAppear { focusedField = .email }
.onSubmit {
switch focusedField {
case .email: focusedField = .password
case .password, nil: submit()
}
}
}
}将焦点状态限定在拥有可焦点控件的视图内部。
Default Focus
默认焦点
Use to set the preferred initial focus region or control when a view appears or when focus is reassigned automatically.
.defaultFocusswift
struct SidebarView: View {
enum Target: Hashable { case library, settings }
@FocusState private var focusedTarget: Target?
var body: some View {
VStack {
Button("Library") { }
.focused($focusedTarget, equals: .library)
Button("Settings") { }
.focused($focusedTarget, equals: .settings)
}
.defaultFocus($focusedTarget, .library)
}
}Prefer one clear default destination per screen or focus region.
使用在视图出现或焦点自动重新分配时,设置首选的初始焦点区域或控件。
.defaultFocusswift
struct SidebarView: View {
enum Target: Hashable { case library, settings }
@FocusState private var focusedTarget: Target?
var body: some View {
VStack {
Button("Library") { }
.focused($focusedTarget, equals: .library)
Button("Settings") { }
.focused($focusedTarget, equals: .settings)
}
.defaultFocus($focusedTarget, .library)
}
}每个屏幕或焦点区域最好设置一个清晰的默认焦点目标。
Focused Values and Scene Values
焦点值与场景值
Use focused values to expose state from the currently focused view. Use scene-focused values when commands or scene-wide UI should keep access to the value even after focus moves within that scene.
swift
struct SelectedRecipeKey: FocusedValueKey {
typealias Value = Binding<Recipe>
}
extension FocusedValues {
var selectedRecipe: Binding<Recipe>? {
get { self[SelectedRecipeKey.self] }
set { self[SelectedRecipeKey.self] = newValue }
}
}
struct RecipeDetailView: View {
@Binding var recipe: Recipe
var body: some View {
Text(recipe.title)
.focusedSceneValue(\.selectedRecipe, $recipe)
}
}Use this pattern for menus, commands, and toolbars that need to act on the focused scene's current content.
使用焦点值暴露当前焦点视图的状态。当命令或场景级UI需要在焦点在场景内移动后仍能访问该值时,使用场景焦点值。
swift
struct SelectedRecipeKey: FocusedValueKey {
typealias Value = Binding<Recipe>
}
extension FocusedValues {
var selectedRecipe: Binding<Recipe>? {
get { self[SelectedRecipeKey.self] }
set { self[SelectedRecipeKey.self] = newValue }
}
}
struct RecipeDetailView: View {
@Binding var recipe: Recipe
var body: some View {
Text(recipe.title)
.focusedSceneValue(\.selectedRecipe, $recipe)
}
}此模式适用于需要对焦点场景的当前内容执行操作的菜单、命令和工具栏。
Focusable Interactions
可焦点交互
Use on custom SwiftUI views that should participate in keyboard or directional focus.
.focusable(_:interactions:)swift
struct SelectableCard: View {
let title: String
let action: () -> Void
@FocusState private var isFocused: Bool
var body: some View {
Button(action: action) {
RoundedRectangle(cornerRadius: 12)
.fill(isFocused ? Color.accentColor.opacity(0.15) : .clear)
.overlay { Text(title) }
}
.buttonStyle(.plain)
.focusable(interactions: .activate)
.focused($isFocused)
}
}Use for button-like controls. Reserve broader interactions for views that genuinely need editing or multiple focus-driven behaviors.
.activate在需要参与键盘或方向焦点的自定义SwiftUI视图上使用。
.focusable(_:interactions:)swift
struct SelectableCard: View {
let title: String
let action: () -> Void
@FocusState private var isFocused: Bool
var body: some View {
Button(action: action) {
RoundedRectangle(cornerRadius: 12)
.fill(isFocused ? Color.accentColor.opacity(0.15) : .clear)
.overlay { Text(title) }
}
.buttonStyle(.plain)
.focusable(interactions: .activate)
.focused($isFocused)
}
}类按钮控件使用。只有真正需要编辑或多种焦点驱动行为的视图才使用更广泛的交互类型。
.activateFocus Sections
焦点区域
Use on macOS 13+ and tvOS 15+ to guide directional movement across groups of focusable descendants in uneven layouts.
focusSection()swift
struct TVLibraryView: View {
var body: some View {
HStack {
VStack {
Button("Recent") { }
Button("Favorites") { }
Button("Downloaded") { }
}
.focusSection()
VStack {
Button("Featured") { }
Button("Top Picks") { }
Button("Continue Watching") { }
}
.focusSection()
}
}
}Use focus sections on macOS and tvOS when default left/right or up/down movement skips the intended group.
在macOS 13+和tvOS 15+上使用,引导焦点在布局不均匀的可焦点子视图组之间进行方向移动。
focusSection()swift
struct TVLibraryView: View {
var body: some View {
HStack {
VStack {
Button("Recent") { }
Button("Favorites") { }
Button("Downloaded") { }
}
.focusSection()
VStack {
Button("Featured") { }
Button("Top Picks") { }
Button("Continue Watching") { }
}
.focusSection()
}
}
}当默认的左右或上下焦点移动跳过了预期的视图组时,在macOS和tvOS上使用焦点区域。
Focus Restoration
焦点恢复
After dismissing a sheet, popover, or transient overlay, return focus to a stable trigger or logical next target.
swift
struct FiltersView: View {
@State private var showSheet = false
@FocusState private var isFilterButtonFocused: Bool
var body: some View {
Button("Filters") { showSheet = true }
.focused($isFilterButtonFocused)
.sheet(isPresented: $showSheet) {
FilterEditor()
.onDisappear {
Task { @MainActor in
isFilterButtonFocused = true
}
}
}
}
}Restore focus intentionally whenever presentation changes would otherwise leave users disoriented.
在关闭弹窗、弹出层或临时覆盖视图后,将焦点返回到稳定的触发控件或逻辑上的下一个目标。
swift
struct FiltersView: View {
@State private var showSheet = false
@FocusState private var isFilterButtonFocused: Bool
var body: some View {
Button("Filters") { showSheet = true }
.focused($isFilterButtonFocused)
.sheet(isPresented: $showSheet) {
FilterEditor()
.onDisappear {
Task { @MainActor in
isFilterButtonFocused = true
}
}
}
}
}每当视图展示变更可能使用户迷失方向时,都要主动恢复焦点。
UIKit Focus Guides
UIKit焦点引导
Use when UIKit or tvOS layouts need custom routing across empty space or awkward geometry.
UIFocusGuideswift
final class DashboardViewController: UIViewController {
private let focusGuide = UIFocusGuide()
@IBOutlet private weak var leadingButton: UIButton!
@IBOutlet private weak var trailingButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.addLayoutGuide(focusGuide)
focusGuide.preferredFocusEnvironments = [trailingButton]
NSLayoutConstraint.activate([
focusGuide.leadingAnchor.constraint(equalTo: leadingButton.trailingAnchor),
focusGuide.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor),
focusGuide.topAnchor.constraint(equalTo: leadingButton.topAnchor),
focusGuide.bottomAnchor.constraint(equalTo: leadingButton.bottomAnchor)
])
}
}UIFocusGuide当UIKit或tvOS布局需要跨空白区域或不规则几何结构进行自定义焦点路由时,使用。
UIFocusGuideswift
final class DashboardViewController: UIViewController {
private let focusGuide = UIFocusGuide()
@IBOutlet private weak var leadingButton: UIButton!
@IBOutlet private weak var trailingButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.addLayoutGuide(focusGuide)
focusGuide.preferredFocusEnvironments = [trailingButton]
NSLayoutConstraint.activate([
focusGuide.leadingAnchor.constraint(equalTo: leadingButton.trailingAnchor),
focusGuide.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor),
focusGuide.topAnchor.constraint(equalTo: leadingButton.topAnchor),
focusGuide.bottomAnchor.constraint(equalTo: leadingButton.bottomAnchor)
])
}
}UIFocusGuideCommon Mistakes
常见错误
- Mixing accessibility focus and keyboard or directional focus in the same mental model.
- Storing in shared models instead of the owning view.
@FocusState - Setting multiple competing default focus targets on one screen.
- Using on decorative views.
.focusable() - Forgetting focus restoration after sheets, popovers, or custom overlays.
- Reaching for before trying
UIFocusGuideon macOS or tvOS, or better layout grouping in SwiftUI.focusSection() - Using gesture handlers for primary actions on custom focusable controls instead of a semantic when possible.
Button
- 将无障碍焦点与键盘或方向焦点混为一谈。
- 在共享模型中存储,而非在拥有控件的视图中存储。
@FocusState - 在一个屏幕上设置多个相互冲突的默认焦点目标。
- 在装饰性视图上使用。
.focusable() - 关闭弹窗、弹出层或自定义覆盖视图后忘记恢复焦点。
- 在macOS或tvOS上未尝试,也未优化SwiftUI布局分组就直接使用
focusSection()。UIFocusGuide - 在自定义可焦点控件上使用手势处理器处理主要操作,而非尽可能使用语义化的。
Button
Review Checklist
检查清单
- is local to the view that owns the controls
@FocusState - Initial focus target is explicit when the screen needs one
- Focus movement between fields or groups is deterministic
- or related focused-value APIs are used when commands need current scene state
focusedSceneValue - Custom controls opt into focus only when they are truly interactive
- is used for uneven directional layouts on macOS or tvOS before dropping to UIKit
focusSection() - Focus returns to a stable element after temporary presentations dismiss
- geometry and preferred destinations match the intended route
UIFocusGuide - Accessibility focus concerns are handled in , not mixed into keyboard-directional focus logic
ios-accessibility
- 限定在拥有控件的视图内部
@FocusState - 当屏幕需要初始焦点时,明确设置初始焦点目标
- 字段或视图组之间的焦点移动具有确定性
- 当命令需要当前场景状态时,使用或相关的焦点值API
focusedSceneValue - 自定义控件仅在真正具有交互性时才启用焦点
- 在macOS或tvOS上,针对不均匀的方向布局,先使用再降级到UIKit实现
focusSection() - 临时视图关闭后,焦点返回到稳定的元素
- 的几何结构和首选目标与预期的路由匹配
UIFocusGuide - 无障碍焦点相关问题在中处理,未与键盘/方向焦点逻辑混淆
ios-accessibility
References
参考资料
- Detailed patterns: references/focus-patterns.md
- Multi-platform focus (tvOS, watchOS, visionOS, macOS): references/multi-platform-focus.md
- Focus debugging and anti-patterns: references/focus-debugging.md
- 详细模式:references/focus-patterns.md
- 多平台焦点(tvOS、watchOS、visionOS、macOS):references/multi-platform-focus.md
- 焦点调试与反模式:references/focus-debugging.md