iOS VoiceOver:屏幕显示时如何让焦点定位到导航栏标题?
让VoiceOver默认聚焦导航栏标题的更优方案
默认情况下VoiceOver会优先聚焦导航栏的返回按钮,要让它改为聚焦标题,你可以尝试以下两种更可靠的方案,避免临时方案的弊端:
方案1:在ViewController中直接指定标题元素作为聚焦目标
通过遍历导航栏子视图找到系统标题对应的视图,再发送screenChanged通知直接指定聚焦该元素,比发送给整个导航栏更精准:
extension UIViewController { func focusNavigationBarTitleOnAppear() { guard let navigationBar = navigationController?.navigationBar else { return } // 定位系统默认的标题视图(私有类UINavigationBarTitleView) let titleView = navigationBar.subviews.first { String(describing: type(of: $0)).contains("UINavigationBarTitleView") } if let targetElement = titleView { UIAccessibility.post(notification: .screenChanged, argument: targetElement) } } } // 在需要的ViewController中调用 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) focusNavigationBarTitleOnAppear() }
这个方案不需要自定义标题视图,通过类名匹配定位标题元素,比硬编码子视图索引更稳定,适配不同iOS版本的导航栏结构变化。
方案2:全局修改导航栏的可访问元素顺序
如果需要所有页面都生效,可以给UINavigationBar写扩展,调整其可访问元素的优先级,让标题元素排在最前面:
extension UINavigationBar { override open func layoutSubviews() { super.layoutSubviews() guard var accessibilityElements = self.accessibilityElements else { return } // 找到标题对应的可访问元素 guard let titleElement = accessibilityElements.first(where: { guard let view = $0 as? UIView else { return false } return String(describing: type(of: view)).contains("UINavigationBarTitleView") }) else { return } // 将标题元素移到可访问数组的首位,提升优先级 accessibilityElements.removeAll { $0 === titleElement } accessibilityElements.insert(titleElement, at: 0) self.accessibilityElements = accessibilityElements } }
这样所有使用系统导航栏的页面,VoiceOver都会默认优先聚焦标题,无需在每个ViewController中重复写代码,适配性更强。
方案对比
- 方案1适合局部页面调整,灵活性高;
- 方案2适合全局统一配置,减少重复代码。
两种方案都避免了临时方案中“移除再恢复返回按钮”的闪烁问题,以及硬编码索引的不稳定问题,同时不需要自定义导航标题视图。
内容的提问来源于stack exchange,提问作者Heuristic
相关产品推荐
相关产品推荐

