UIStackView 可访问性 - 在默认的可访问元素中插入可访问性视图?

Posted

技术标签:

【中文标题】UIStackView 可访问性 - 在默认的可访问元素中插入可访问性视图?【英文标题】:UIStackView accessibility - Insert an accessibility view in default accessibleElements? 【发布时间】:2021-04-15 21:03:04 【问题描述】:

现在了解到 UIStackView 的可访问性默认情况下会在语音开启时按顺序读取排列的子视图。假设我有这个层次结构:

UIView
   - UIStackView
       -view1
       -view2
       -view3
       -view4
   - collapsibleSpecialView

collapsibleSpecialView 锚定在 view4 的顶部:

        etc
|------------------|
|      view3       |
|------------------|
|    collapsible   |     <- when collapsed overlaps with vw3 to vw1
|------------------|
|      view4       |
|------------------|

我需要配音才能按上述顺序阅读我的元素。但是由于 stackView 需要首先完成其所有可访问的子视图,所以我的可折叠视图是最后读取的,但我需要在 view4 之前读取它。

我无法将它添加到 stackview,因为当 collapsibleView 折叠时,它会将所有内容推到上面。而且我不想破解自动布局来解决这个问题。

我认为可行的方法是在 view4 之前插入一个不可见的代理 UIView,并在该 ProxyView 中覆盖其 methods 以返回可访问的元素。

public class ProxyView: UIView 
    private var view: UIView?

    /// view is the collapsibleView
    @objc public convenience init(view: UIView) 
        self.init()
        self.isAccessibilityElement = true
        self.view = view
    

    public override func accessibilityElementCount() -> Int 
        return 1
    

    public override func accessibilityElement(at index: Int) -> Any? 
        guard let menu = view else  return nil 
        return menu
    

    public override func index(ofAccessibilityElement element: Any) -> Int 
        return 0
    

有了这个画外音,可以在view4 之前读取我的 ProxyView,但由于某种原因它没有进入 collapsibleView,向右滑动会将焦点转移到 view4。

如果我对 UIAccessibilityContainer 的理解是正确的,理论上这应该可行,但事实并非如此。我错过了什么?谢谢

【问题讨论】:

【参考方案1】:

我能够解决这个问题。我的解决方案几乎就是我上面的解决方案,但有一些修复。

class CollapsibleView: UIView 
     /// set isAccessibilityElement = true in init
    
     /// set it accessibilityElements in order you want

     /// this is to tell the  VoiceOver not to capture 
     /// our subviews because we are going to pass them to our proxy
     /// setting this to false will cause VO to loop back to 
     /// this view's subviews.    


class ProxyView: UIView 
    var lastFocusedElement: Any?
    
    private var view: CollapsibleView!
    
    /// Collapsible view is accessible element and so VO will still focus on it. We pass the focus to last focused element since we want view before collapsibleView be the last focuseable element.

    /// If you have other views to focus after your view, you have to figure out how to tell VO to skip collapsibleView without setting its isAccessibilityElement to false
    @objc func voFocused(notification: Notification) 
        guard let focused = notification.userInfo?[UIAccessibility.focusedElementUserInfoKey] else 
            return
        

        if let asView = focused as? UIView, asView == view 
            ///change focus to last
            UIAccessibility.post(notification: .screenChanged, argument: lastFocusedElement)
         else 
            lastFocusedElement = focused
        
    

    required init?(coder: NSCoder) 
        super.init(coder: coder) 
        /// This is an invisible container-only view. We
        /// It needs to be set to false to allow VO
        /// Recognize it as a container. 
        self.isAccessibilityElement = false
        NotificationCenter.default.addObserver(self, selector: #selector(voFocused(notification:)),
                                               name: UIAccessibility.elementFocusedNotification,
                                               object: nil)

    
    
    func setView(vw: CollapsibleView)
        self.view = vw
        self.accessibilityElements = view.accessibilityElements
    

剩下的就是将 ProxyView() 添加到我们希望它宣布的任何顺序的堆栈

【讨论】:

以上是关于UIStackView 可访问性 - 在默认的可访问元素中插入可访问性视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 QMenu 中包含的 QActions 的可访问名称

允许/阻止标签的可访问性字体大小调整

如何在 CMFCMenuBar 及其项目中支持可访问性

如何理解“子类不能降低父类中定义的方法的可访问性”这句话?

如何在其 isAccessibilityElement = false 时禁用元素的可访问性焦点

加载数据时的可访问性