我正在尝试确定发布UIAccessibilityLayoutChangedNotificationUIAccessibilityScreenChangedNotification时到底发生了什么变化。从我所看到的,我可以在任何地方互换使用它们,而没有发生任何不同的情况。

Apple文档只是说要在隐藏或显示某个元素时使用LayoutChanged,而在整个屏幕更改时都使用ScreenChanged,但我对提供此信息时它们会做什么以及应该做什么感兴趣。使用另一种时看到的情况有所不同。

谁能明确解释两者之间的实现差异?

最佳答案

这两个通知是针对 View 上的动态内容的,并将这些更改传达给屏幕阅读器用户的VoiceOver。这两个通知之间没有什么区别,除了它们的默认行为和ScreenChange通知的愚蠢的“哔哔”声。

在这两种情况下,

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, arg);

表示要读出的字符串或屏幕上的元素,VoiceOver会将焦点转移到该元素上。在发生急剧变化的情况下,重要的是将焦点转移到有意义的地方,或者宣布发生了这种变化。从可访问性的角度来看,这两种方法都是可以接受的,尽管我更喜欢涉及尽可能少的更改的方法。在简单的布局更改的情况下,几乎总是最好只是宣布上下文更改,并将焦点留在原处。尽管有时会隐藏导致上下文更改的元素,然后显然有必要指导配音以突出显示新内容,因为这种情况下的默认行为是不确定的,或者可能是确定性的,但由完全不了解的框架确定关于您的应用程序!

考虑到它们都做完全相同的事情,这两个事件之间的区别在于它们的默认行为。如果您为UIAccessibilityLayoutChangedNotification提供nil,就好像您什么都没做。如果为UIAccessibilityScreenChangedNotification提供了nil参数,则一旦所有 View 层次结构更改并绘制完成,它将把焦点发送到 View 层次结构中标记为accessibilityElement的第一个UIObject。

UIAccessibilityLayoutChangedNotification
UIAccessibilityLayoutChangedNotification的一个很好的用例是动态表单。您想让用户知道,根据他们在表单中做出的决定,可以使用新的选项。例如,如果您在表单中选择自己是退伍军人,则该表单的其他区域可能会弹出以提供更多输入,但是这些区域可能已被不关心它们的其他用户隐藏。因此,您可以在用户交互后将重点转移到这些元素上:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, firstNewFormElement);

它将把焦点转移到提供的元素上,并宣布它的accessibilityLabel。

或者只是告诉他们那里有新的表单元素:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, @"Veterans form elements available");

这将使焦点保持在原处,但是VoiceOver会宣布“可以使用退伍军人表格元素”。

注意:此特殊行为在我的iPad(8.1.2)上存在错误。

或最终,您可以执行以下操作:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);

绝对不做任何事情:)。认真地说,我什至不认为a11y框架后端很在乎。这特定的代码行完全是浪费!

UIAccessibilityScreenChangedNotification
UIAccessibilityScreenChangedNotification的一个很好的用例是定制的选项卡式浏览情况。当整个屏幕(导航区域除外)发生变化时。您想让画外音知道整个屏幕实际上已更改,但不要聚焦第一个元素(您的第一个标签),而是聚焦第一个内容元素。
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstNonGlobalNavElement);

它将播放“哔哔”声,然后将焦点移到全局导航栏的正下方。或者,您可以这样做:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @"You're on a new tab");

这将等待新选项卡加载,播放“哔哔声”声音,在画外音中宣布“您在新选项卡上”,然后将焦点转移到屏幕上的第一个元素,然后宣布该元素的accessibilityLabel。 (PHEW!太多了!这对于屏幕阅读器用户来说是很讨厌的。除非绝对必要,否则请避免这种情况)。

最后,您当然可以这样做:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);

等效于:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstA11yElement);

两者都将播放“哔哔”声,将VoiceOver焦点移到屏幕上的第一个元素,然后宣布。

最后

在某人的评论中提到了缓存,我偶尔在回答中评论有关A11y后端可能关心或可能不关心的事情。尽管肯定有可能发生一些后端魔术,但我不相信在这两种情况下,后端都在乎。我之所以这样说是因为:

如果您曾经使用过UIAccessibilityContainer协议(protocol),则可以观看查询您的 View 容器的过程。没有缓存正在进行。每次VoiceOver将焦点更改为容器中的新AccessibilityElement时,甚至accessibilityElementCount属性都将被ping通。然后,它经历了检查它在哪个元素上,询问下一个元素等等的过程。它的核心设计是处理动态情况。如果您要在交互后在容器中插入一个新元素,它仍然会经历所有这些查询,并且就可以了!此外,如果您覆盖UIAccessibility协议(protocol)的属性,则为了提供动态提示和标签,您还可以看到每次调用这些函数!因此,我相信A11y框架后端可以从这些通知中收集绝对零信息。 VoiceOver要做的唯一信息是当前关注的Accessibility元素,以及此元素Accessibility Container。通知只是为您提供的,以使您的应用更适合VoiceOver用户使用。

想象一下,如果不是这种情况,那么Safari会发布这些通知多少次!!! :)

这些特定的语句只能由具有框架的后端知识的人员来确认,并且可以使用该代码,并且应将其视为推测。这可能是高度依赖版本/实现的情况。绝对可以就这些问题进行讨论!这篇文章的其余部分非常具体。

供您引用

其中大部分来自使用框架的经验,但是如果您想进一步研究,这里是有用的引用。

https://developer.apple.com/documentation/uikit/accessibility/uiaccessibility

https://developer.apple.com/documentation/uikit/uiaccessibilitylayoutchangednotification

https://developer.apple.com/documentation/uikit/uiaccessibilityscreenchangednotification

最后,我整理了一个愚蠢的小应用程序的开放源代码 repo ,以测试所有这些东西。

https://github.com/chriscm2006/IOS-A11y-Api-Test

10-05 20:46
查看更多