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

求助:Voice Over返回页面后无法恢复上次焦点位置的解决方法

解决Voice Over返回页面后无法恢复焦点位置的问题

这个问题确实是VoiceOver的默认行为——每次页面重新加载(包括返回或dismiss后),它都会自动聚焦到你设置的accessibilityElements数组的第一个元素。要让它记住上次的焦点位置,我们需要手动保存和恢复焦点状态,下面是具体的实现方案:

1. 保存上次聚焦的无障碍元素

首先在你的ViewController中添加一个属性,用来存储最后一次获得焦点的无障碍元素:

private var lastFocusedAccessibilityElement: UIView?

然后通过监听VoiceOver的焦点变化通知,实时更新这个属性:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 保持你原有的无障碍元素顺序配置
    horizontalScroller.accessibilityElements = [firstQuestion, secondQuestion, thirdQuestion, fourthQuestion, fifthQuestion, sixthQuestion, seventhQuestion, eigthQuestion, ninthQuestion, finalQuestion]
    view.accessibilityElements = [titlelabelLabel, closebuttonButton, nextButton, backButton, horizontalScroller]
    
    // 监听无障碍元素焦点变更通知
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(accessibilityElementFocused(_:)),
        name: UIAccessibility.elementFocusedNotification,
        object: nil
    )
}

@objc private func accessibilityElementFocused(_ notification: Notification) {
    guard let focusedElement = notification.object as? UIView else { return }
    
    // 只保存我们页面内的相关元素(避免保存其他无关的系统元素)
    let relevantElements = (view.accessibilityElements as? [UIView] ?? []) + (horizontalScroller.accessibilityElements as? [UIView] ?? [])
    if relevantElements.contains(focusedElement) {
        lastFocusedAccessibilityElement = focusedElement
    }
}

2. 在页面显示时恢复焦点

重写viewWillAppear方法,当页面再次出现(包括从其他页面返回、dismiss控制器A后),自动将VoiceOver焦点恢复到上次的元素:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // 延迟执行确保UI已完全布局完成
    DispatchQueue.main.async { [weak self] in
        guard let self = self, let focusedElement = self.lastFocusedAccessibilityElement else { return }
        // 通知VoiceOver将焦点转移到目标元素
        UIAccessibility.post(notification: .layoutChanged, argument: focusedElement)
    }
}

3. 优化控制器A的present场景

在触发present控制器A的方法里,我们可以主动保存当前焦点,确保dismiss后能精准恢复:

func presentControllerA() {
    // 主动保存当前焦点元素
    lastFocusedAccessibilityElement = UIAccessibility.focusedElement as? UIView
    
    let controllerA = ControllerA()
    present(controllerA, animated: true)
}

注意事项

  • 确保存储的lastFocusedAccessibilityElement是当前视图层级中的有效元素,避免因视图销毁导致的引用失效
  • 如果你的scrollview子视图是动态生成的,要确保在保存焦点时这些元素已经完成初始化并添加到视图中
  • 使用DispatchQueue.main.async是为了避开视图布局未完成的时机,确保VoiceOver能正确找到目标元素

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 18:12:35