SwiftUI可访问性:关闭Modal后VoiceOver焦点未停留原按钮问题
SwiftUI Modal关闭后VoiceOver焦点回退到触发按钮的实现方案
系统默认在sheet模态视图关闭时,会将VoiceOver焦点自动重置到页面左上角的首个元素,要实现关闭弹窗后焦点仍停留在原触发按钮上的效果,需要手动接管可访问性焦点逻辑,具体实现步骤如下:
步骤1:声明焦点状态与位置标识
在触发按钮所在的父视图层级,使用iOS15起提供的AccessibilityFocusState绑定VoiceOver焦点位置,搭配枚举标记不同的可聚焦点位,避免零散状态带来的逻辑混乱:
// 视图内的状态定义 @AccessibilityFocusState private var focusLocation: AppFocus? @State private var showModal = false // 标记所有需要手动控制焦点的位置 enum AppFocus { case modalTriggerButton }
步骤2:为触发按钮关联焦点绑定
给原本触发Modal弹出的按钮添加焦点绑定修饰符,让系统识别该按钮对应的焦点位置:
Button("打开设置弹窗") { showModal = true } .accessibilityFocused($focusLocation, equals: .modalTriggerButton)
步骤3:在Modal关闭时机主动回设焦点
给sheet修饰符添加视图消失监听,在Modal完全从视图层级移除后,主动将焦点设置回触发按钮位置:
.sheet(isPresented: $showModal) { SettingsModalView() .onDisappear { // 延迟等待Modal视图完全从窗口层级移除,避免焦点设置被系统默认的迁移逻辑覆盖 DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { focusLocation = .modalTriggerButton } } }
常见踩坑提醒
- 不要在sheet的
onDismiss回调中直接设置焦点:onDismiss的触发时机早于视图完全销毁的节点,此时设置的焦点会被系统后续自动触发的左上角焦点跳转逻辑覆盖。延迟时长不需要太精确,只要覆盖当前机型上sheet消失动画的总时长即可,过短会导致焦点设置失效,过长会让用户感知到明显的焦点跳转延迟 - 如果需要兼容iOS14及更早版本,可以用系统原生的可访问性通知API实现同等效果,在对应时机调用:
UIAccessibility.post(notification: .layoutChanged, argument: 触发按钮对应的可访问性元素) - 不要把
AccessibilityFocusState的声明放在Modal视图内部:焦点状态的作用域需要覆盖触发按钮和Modal视图,跨层级的焦点绑定会出现失效问题
内容的提问来源于stack exchange,提问作者amey rane
相关产品推荐
相关产品推荐

