You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI弹窗与AccessibilityFocusState焦点无法正确切换求助

解决SwiftUI Alert关闭后VoiceOver焦点异常跳转问题

问题分析

直接在Alert按钮点击事件中设置AccessibilityFocusState无效,是因为弹窗仍处于活跃状态时,主视图元素还未重新获得可访问性焦点权限;而用Alert的onDisappear会导致VoiceOver先读取系统默认恢复的焦点元素,再切换到目标焦点,造成用户困惑。核心矛盾在于:弹窗关闭时系统会自动恢复默认焦点,我们需要在这个时机之后覆盖焦点设置,同时确保操作在视图更新周期内生效。

解决方案

通过存储待设置的焦点目标,在弹窗完全关闭后(主视图重新激活),异步触发焦点切换,覆盖系统默认行为。具体步骤如下:

  1. 添加一个状态变量存储待跳转的焦点目标
  2. 在Alert按钮中仅记录目标焦点,不直接设置
  3. 监听弹窗显示状态的变化,在弹窗关闭后异步设置目标焦点

修改后的完整代码

struct MyView: View {
    enum AccessFocusValues {
        case section1
        case section2
    }

    @AccessibilityFocusState private var accessFocus: AccessFocusValues?
    @State private var showAlert: Bool = false
    @State private var pendingFocus: AccessFocusValues?

    var body: some View {
        VStack(spacing: 20) {
            Text("Section 1")
                .accessibilityFocused($accessFocus, equals: .section1)
                .font(.title)

            Button {
                showAlert = true
            } label: {
                Text("Tap to Delete")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            .alert(
                Text("My Title"),
                isPresented: $showAlert,
                actions: {
                    Button("Cancel", role: .cancel) {
                        pendingFocus = .section1
                    }

                    Button("Delete", role: .destructive) {
                        pendingFocus = .section2
                        // 在这里执行你的删除逻辑
                    }
                },
                message: {
                    Text("My message")
                }
            )

            Text("Section 2")
                .accessibilityFocused($accessFocus, equals: .section2)
                .font(.title)
        }
        .onChange(of: showAlert) { oldValue, newValue in
            // 弹窗关闭时(newValue为false),执行焦点设置
            if !newValue, let targetFocus = pendingFocus {
                Task { @MainActor in
                    accessFocus = targetFocus
                }
                pendingFocus = nil
            }
        }
    }
}

关键说明

  • pendingFocus的作用:避免在Alert活跃时直接操作主视图的焦点,确保只有当弹窗完全关闭、主视图重新激活后才触发焦点切换。
  • Task @MainActor的必要性:确保焦点设置操作在主队列执行,并且等待视图更新周期完成,能够覆盖系统默认的焦点恢复行为,避免VoiceOver先读取其他元素。
  • 原代码中accessibilityFocused的枚举case需补全.(如.section1),否则会编译报错。

内容的提问来源于stack exchange,提问作者kittonian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:07:28