这两个通知针对视图上的动态内容,并将这些更改传递给屏幕阅读器用户的VoiceOver。这两个通知之间几乎没有什么区别,除了它们的默认行为,以及ScreenChange通知的傻小小“嘟嘟声”。
在这两种情况下,所述参数
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, arg);
表示一个字符串被读出,或屏幕元素,这将的VoiceOver其重点转向上。在发生剧烈的情境变化时,将焦点转移到有意义的地方或宣布发生了此类变化是很重要的。从可访问性的角度来看,任何一种方法都是可以接受的,尽管我更喜欢那些涉及最少可能变化的方法。在简单的布局改变的情况下,几乎总是最好的宣布上下文更改,并将焦点留在原来的位置。虽然有时候,导致上下文更改的元素是隐藏的,但是显然有必要指定配音来突出显示新内容,因为在这种情况下默认行为是未定义的,或者是确定性的,但是由一个完全不知道的框架决定关于你的应用!
这两个事件之间的区别,假设它们都做同样的事情,是在它们的默认行为。如果你提供零到UIAccessibilityLayoutChangedNotification
就好像你没有做任何事情。如果您向UIAccessibilityScreenChangedNotification
提供零参数,则会在所有视图层次更改和绘图完成后,将焦点发送到视图层次结构中标记为accessibilityElement的第一个UIObject。
UIAccessibilityLayoutChangedNotification
为UIAccessibilityLayoutChangedNotification
一个很好的使用情况实例是用于动态形式。您希望让用户知道,根据他们在表单中做出的决定,新选项可用。例如,如果在表单中选择自己是退伍军人,则可能会弹出表单的其他区域以提供更多输入,但这些区域可能已隐藏给其他不关心它们的用户。所以,你可以转移重心到用户交互之后这些元素:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, firstNewFormElement);
这将焦点切换到提供的元素,并宣布它的accessibilityLabel。
或者只是告诉他们,新的表单元素有:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, @"Veterans form elements available");
这会使注重它在哪里,但VoiceOver会会宣布“退伍军人组成可用元素”。
注意:此特定行为在我的iPad(8.1.2)上被窃听。
或者最后你可以这样做:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
这也绝对没有什么:)。说真的,我甚至不认为这个框架后端关心。这一行代码是一个完整的浪费!
UIAccessibilityScreenChangedNotification
为UIAccessibilityScreenChangedNotification
一个很好的使用情况实例是定制的标签浏览的情况。当整个屏幕(除导航区域外)发生变化时。您希望让配音员知道基本上整个屏幕都发生了变化,但不会将第一个元素(您的第一个标签)聚焦,而是专注于第一个内容元素。
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstNonGlobalNavElement);
这将播放“嘟嘟声”的声音,然后将焦点转移到您的全局导航栏下方。或者你可以这样做:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @"You're on a new tab");
这将等待新的标签,加载,发挥“嘟BOOP”的声音,宣布“你是一个新的选项卡上的”画外音中,然后将焦点切换到第一元素,然后宣布该元素的accessibilityLabel。 (PHEW!这太多了!这对于屏幕阅读器用户来说是很刺激的。避免这种情况,除非绝对必要)。
最后,你当然可以这样做:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
即相当于:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstA11yElement);
这两者将发挥“嘟BOOP”的声音,VoiceOver的重点转移到第一元素在屏幕上,然后宣布它。
最后
在评论中提到有人缓存,并且我偶尔在我对事物的A11Y后端可能会或可能不会在意答案评论。尽管发生后端魔术当然有可能发生,但我不相信这两种情况之一,后端根本就不在乎。我之所以这么说是因为:
如果你曾经使用过UIAccessibilityContainer
协议,你可以看你的视图容器被查询。没有缓存正在进行。每次VoiceOver将焦点更改为容器中新的AccessibilityElement时,即使accessibilityElementCount
属性也会被触发。然后它检查它所在的元素,询问下一个元素,等等。它的核心是处理动态情况。如果在交互之后要将新元素插入到容器中,它仍然会经历所有这些查询,并对其进行处理!此外,如果您重写UIAccessibility协议的属性,为了提供动态提示和标签,您还可以看到每次都会调用这些函数!因此,我相信A11y Framework后端从这些通知中搜集绝对零度的信息。 VoiceOver需要完成其工作的唯一信息是当前专注的辅助功能元素,以及此元素的辅助功能容器。这些通知只是为了让您的应用程序更适用于VoiceOver用户。
想象一下,如果不是这种情况,Safari会发布多少次这些通知! :)
这些特定的声明只能由具有该框架的后端知识的人员确认,该人员使用该代码,并应将其视为猜测。可能是这种情况是高度版本/实施依赖性的。肯定会对这些观点进行讨论!这篇文章的其余部分非常具体。
供您参考
这其中大部分来自于与框架的工作经验,但这里是一个有用的参考,如果你想进一步挖掘。
https://developer.apple.com/documentation/uikit/accessibility/uiaccessibility
https://developer.apple.com/documentation/uikit/uiaccessibilitylayoutchangednotification
https://developer.apple.com/documentation/uikit/uiaccessibilityscreenchangednotification
最后,愚蠢的小应用程序,我放在一起测试所有这些东西的一个开源回购。
https://github.com/chriscm2006/IOS-A11y-Api-Test
钉住它。对'UIAccessibilityContainer'进行测试的好主意。 – Justin
完美。谢谢。我同意@Justin。我还转载了你提到的并提交了雷达的错误。 –
很好的答案。关于缓存的部分,我只有轻微的不同意见。我100%肯定某处有某种缓存。说明:我对'UIAccessibilityContainer'做了一些测试,就像你说的那样,容器方法在焦点移动时被调用。但是,无论容器协议方法返回什么,在某些情况下,如果您没有明确发布布局/屏幕更改通知,则配音会一直读取错误的值。所以有引擎盖下的缓存。它可能不会单独缓存元素,但它会缓存(元素,位置)对或这些行周围的内容。 –