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 的可访问名称