Focus Engine

作者 dpearson26998d90fd121a26無授權條款1.1K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Implements keyboard, directional, and scene-level focus behavior across SwiftUI and UIKit. Use when managing @FocusState, defaultFocus, focused values, focusable interactions, focus sections, tvOS geometric focus and Siri Remote navigation, watchOS Digital Crown input, visionOS connected-device focus versus gaze hover/input targets, macOS key view loop and Full Keyboard Access, focus restoration after presentation changes, custom focus routing with UIFocusGuide, or debugging focus with UIFocusDebugger.

AI 產生的概覽

指導 SwiftUI 與 UIKit 應用程式在 Apple 各平台上的鍵盤、方向與場景層級焦點行為。

功能
此技能提供在 SwiftUI 和 UIKit 中實作焦點行為的參考指引與程式碼模式,涵蓋 @FocusState、預設焦點、焦點值與場景值、可聚焦互動、焦點區段、焦點還原以及 UIKit 焦點導引。內容也涵蓋平台特定焦點,例如 tvOS 幾何焦點與 Siri Remote 導覽、watchOS 數位錶冠輸入、visionOS 連接裝置焦點與注視懸停的區別,以及 macOS 按鍵檢視循環。內容包含常見錯誤清單、審查清單,並指向隨附的焦點模式、多平台焦點與除錯參考檔案。
適用情境
適用於撰寫或審查涉及鍵盤焦點、方向或遙控器導覽、場景焦點值、呈現後焦點還原或自訂焦點路由的 Apple 平台 UI 程式碼。也適用於區分焦點與輔助功能焦點或 visionOS 注視懸停,以及除錯焦點問題。
執行需求
不含指令碼,僅為說明與參考文件。代理程式無需特殊工具、套件、認證或網路存取,但內容鎖定 iOS 26+、iPadOS、macOS、tvOS 和 visionOS 上的 SwiftUI 與 UIKit。

Focus Engine

Focus behavior for SwiftUI and UIKit apps targeting iOS 26+, iPadOS, macOS, tvOS, and visionOS connected-input paths. Covers keyboard focus, directional focus, scene-focused values, focus restoration, and UIKit focus guides. focusSection() guidance in this skill applies to macOS and tvOS. visionOS gaze-driven hover is an input affordance, not focus. Accessibility-specific focus for VoiceOver and Switch Control lives in the ios-accessibility skill.

When a request mixes focus with accessibility or spatial input, keep the boundary explicit:

  • Use this skill for keyboard, remote, game-controller, and scene focus behavior.
  • For visionOS, describe gaze, direct touch, and pointer targeting as hover/input affordances, not focus.
  • For VoiceOver, Switch Control, Voice Control, or accessibility element ordering, give only a brief handoff to ios-accessibility.

Contents

SwiftUI FocusState

Use @FocusState to read and write focus placement inside a scene. Use Bool for a single target or an optional Hashable enum for multiple targets.

swift
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.

Default Focus

Use .defaultFocus to set the preferred initial focus region or control when a view appears or when focus is reassigned automatically.

swift
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.

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.

Focusable Interactions

Use .focusable(_:interactions:) on custom SwiftUI views that should participate in keyboard or directional focus.

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

Prefer semantic Button, Toggle, TextField, and other system controls before making arbitrary gesture-driven views focusable. Use .focusable(interactions: .activate) for custom button-like controls only when a semantic control cannot express the UI. Reserve broader interactions for views that genuinely need editing or multiple focus-driven behaviors.

Focus Sections

Use focusSection() on macOS 13+ and tvOS 15+ to guide directional movement across groups of focusable descendants in uneven layouts.

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.

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.

UIKit Focus Guides

Use UIFocusGuide when UIKit or tvOS layouts need custom routing across empty space or awkward geometry.

swift
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 is invisible and not a view. Use it to redirect focus without adding decorative UI.

Common Mistakes

  1. Mixing accessibility focus and keyboard or directional focus in the same mental model.
  2. Storing @FocusState in shared models instead of the owning view.
  3. Setting multiple competing default focus targets on one screen.
  4. Using .focusable() on decorative views.
  5. Forgetting focus restoration after sheets, popovers, or custom overlays.
  6. Reaching for UIFocusGuide before trying focusSection() on macOS or tvOS, or better layout grouping in SwiftUI.
  7. Using gesture handlers for primary actions on custom focusable controls instead of a semantic Button when possible.
  8. Treating visionOS gaze hover as focus; reserve focus guidance for connected input such as keyboards and game controllers.

Review Checklist

  • @FocusState is local to the view that owns the controls
  • Initial focus target is explicit when the screen needs one
  • Focus movement between fields or groups is deterministic
  • focusedSceneValue or related focused-value APIs are used when commands need current scene state
  • Custom controls opt into focus only when they are truly interactive
  • focusSection() is used for uneven directional layouts on macOS or tvOS before dropping to UIKit
  • Focus returns to a stable element after temporary presentations dismiss
  • UIFocusGuide geometry and preferred destinations match the intended route
  • visionOS guidance distinguishes connected-device focus from gaze-driven hover or RealityKit input targets
  • Accessibility focus concerns are handled in ios-accessibility, not mixed into keyboard-directional focus logic

References

  • Detailed patterns: references/focus-patterns.md [blocked]
  • Multi-platform focus (tvOS, watchOS, visionOS, macOS): references/multi-platform-focus.md [blocked]
  • Focus debugging and anti-patterns: references/focus-debugging.md [blocked]

來源與署名

來源:dpearson2699/swift-ios-skills位於skills/focus-engine提交8d90fd1

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 dpearson2699/swift-ios-skills 的技能

Widgetkit

dpearson2699

指導實作、審查與改進 iOS、iPadOS、watchOS 與 CarPlay 上的 WidgetKit 小工具與控制項。

Software Development1.1K2 個月前更新

Weatherkit

dpearson2699

指導 iOS 開發者使用 WeatherService 取得 WeatherKit 預報、警報與署名資訊。

Software Development1.1K2 個月前更新

Vision Framework

dpearson2699

Implement computer vision features including text recognition (OCR), face detection, barcode scanning, image segmentation, object tracking, and document scanning in iOS apps. Covers both the modern Swift-native Vision API (iOS 18+) and legacy VNRequest patterns, VisionKit DataScannerViewController for live camera scanning, and CoreMLRequest/VNCoreMLRequest for custom model inference. Use when adding OCR, barcode scanning, face detection, or custom Core ML model inference with Vision.

待分類1.1K2 個月前更新

Tipkit

dpearson2699

Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation.

待分類1.1K2 個月前更新

Tabletopkit

dpearson2699

指導使用 TabletopKit 在 visionOS 上打造多人空間桌遊,涵蓋棋具、座位、動作與 RealityKit 算繪。

Software Development1.1K2 個月前更新

Swiftui Webkit

dpearson2699

指導在 iOS 26 及更新版本的 SwiftUI App 中使用 WebKit for SwiftUI 嵌入與控制網頁內容。

Software Development1.1K2 個月前更新