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

如何实现UISearchBar取消按钮的VoiceOver无障碍适配?

解决UISearchBar在VoiceOver下的键盘与取消按钮无障碍问题

针对你遇到的两个问题——VoiceOver下切换元素后键盘不自动收起、动态显示的取消按钮无法被VoiceOver识别,我给你两个方向的实用解决方案:


一、让取消按钮被VoiceOver正确识别

UISearchBar的取消按钮属于系统私有子视图,动态显示时可能无法被VoiceOver及时扫描到,你可以通过以下两种方法修复:

方法1:手动定位按钮并配置无障碍属性

在调用setShowsCancelButton(true)后,遍历搜索栏的子视图找到取消按钮,手动设置它的无障碍标签和属性:

public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { 
    if UIAccessibilityIsVoiceOverRunning() { 
        self.searchBar?.setShowsCancelButton(true, animated: true)
        
        // 等待动画完成后再查找按钮,确保视图已加载
        DispatchQueue.main.async {
            let cancelButton = searchBar.subviews.flatMap { $0.subviews }.first {
                guard let button = $0 as? UIButton else { return false }
                return button.titleLabel?.text == NSLocalizedString("Cancel", comment: "")
            } as? UIButton
            
            // 配置无障碍属性,让VoiceOver能识别并朗读
            cancelButton?.accessibilityLabel = "取消搜索" // 替换为符合你APP语境的标签
            cancelButton?.accessibilityTraits = .button
            cancelButton?.isAccessibilityElement = true
        }
    } 
}

方法2:发送无障碍布局更新通知

如果不想遍历子视图,可以在按钮显示后发送通知,让VoiceOver重新扫描界面元素:

public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { 
    if UIAccessibilityIsVoiceOverRunning() { 
        self.searchBar?.setShowsCancelButton(true, animated: true)
        
        // 等待动画结束后发送通知,确保按钮已完全显示
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            UIAccessibility.post(notification: .layoutChanged, argument: self.searchBar)
        }
    } 
}

二、替代方案:直接根据VoiceOver焦点状态收起键盘

其实你也可以不用依赖取消按钮,而是监听VoiceOver的焦点变化,当搜索栏失去焦点时自动收起键盘,从根源解决键盘残留的问题:

实现代码示例

override func viewDidLoad() {
    super.viewDidLoad()
    // 监听VoiceOver焦点变化通知
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(handleAccessibilityFocusChange),
        name: UIAccessibility.elementFocusedNotification,
        object: nil
    )
}

@objc private func handleAccessibilityFocusChange(_ notification: Notification) {
    guard UIAccessibilityIsVoiceOverRunning(), 
          let focusedElement = notification.object as? UIView else {
        return
    }
    
    // 如果焦点不在搜索栏及其子视图上,收起键盘
    if !focusedElement.isDescendant(of: searchBar) && !searchBar.isDescendant(of: focusedElement) {
        searchBar.resignFirstResponder()
    }
}

// 记得在页面销毁时移除监听
deinit {
    NotificationCenter.default.removeObserver(
        self,
        name: UIAccessibility.elementFocusedNotification,
        object: nil
    )
}

这个方案不需要额外显示取消按钮,完全根据VoiceOver的操作逻辑自动管理键盘,更符合无障碍用户的使用习惯。


建议在VoiceOver环境下反复测试两种方案,选择最贴合你APP交互逻辑的实现方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:31:03