45

我试图确定发布 aUIAccessibilityLayoutChangedNotification和 a时究竟发生了什么不同UIAccessibilityScreenChangedNotification。据我所见,我可以在任何地方互换使用它们,并且没有什么不同。

Apple 文档只是说LayoutChanged在(例如)隐藏或显示元素时使用,并ScreenChanged在整个屏幕发生变化时使用,但我对他们在提供此信息时所做的事情以及我应该看到的不同感兴趣使用其中一种时。

任何人都可以清楚地解释两者之间的实现差异吗?

4

2 回答 2

70

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

在这两种情况下,论点

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, arg);

表示要读出的字符串或屏幕上的元素,VoiceOver 会将其焦点转移到。如果发生剧烈的上下文变化,重要的是把焦点转移到一个有意义的地方,或者宣布这种变化已经发生。从可访问性的角度来看,这两种方法都是可以接受的,尽管我更喜欢涉及尽可能少的更改的方法。如果发生简单的布局更改,最好只宣布上下文更改,并将焦点留在原处。虽然有时会隐藏导致上下文变化的元素,然后显然需要引导画外音突出显示新内容,因为这种情况下的默认行为是未定义的,或者可能是确定性的,但由一个完全不知道的框架决定关于你的应用程序!

鉴于它们都做完全相同的事情,这两个事件之间的区别在于它们的默认行为。如果你向UIAccessibilityLayoutChangedNotification它提供 nil ,就好像你什么也没做。如果您向它提供 nil 参数UIAccessibilityScreenChangedNotification,一旦所有视图层次结构更改和绘图完成,它将将焦点发送到视图层次结构中标记为可访问性元素的第一个 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");

它将等待新标签加载,播放“哔哔”声音,在画外音中宣布“您在新标签上”,然后将焦点转移到屏幕上的第一个元素,然后宣布该元素的可访问性标签。(呸!太多了!这对屏幕阅读器用户来说很不愉快。避免这种情况,除非绝对必要)。

最后你当然可以这样做:

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);    

这相当于:

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstA11yElement);

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

最后

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

如果您曾经使用过该UIAccessibilityContainer协议,则可以观察您的视图容器被查询。没有缓存正在进行。即便是accessibilityElementCount每次 VoiceOver 将焦点更改到容器中的新 AccessibilityElement 时,都会对属性执行 ping 操作。然后它会检查它在哪个元素上,询问下一个元素,等等。它的核心是为处理动态情况而设计的。如果您在交互后向容器中插入一个新元素,它仍然会通过所有这些查询并且没问题!此外,如果您重写 UIAccessibility 协议的属性,以提供动态提示和标签,您还可以看到这些函数每次都会被调用!因此,我相信 A11y 框架后端从这些通知中收集绝对零信息。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

于 2015-02-05T20:54:20.567 回答
-1

UIAccessibilityScreenChangedNotification是表示整个屏幕发生了变化,VoiceOver 应该重置。

UIAccessibilityLayoutChangedNotification是表示屏幕上的一个或多个(但不是全部)元素发生了变化。

当你的 UI 发生巨大变化时。通常当用户移动到应用程序的不同部分时(导航到不同的屏幕)。VoiceOver 会通过提示音通知用户,并清除其缓存并进行其他准备以处理一组新的可访问性数据。它会提醒 VoiceOver 屏幕已更改并且屏幕上可能有新元素,因此 VoiceOver 将重建它的辅助功能元素索引。

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);

如果你的 UI 的某些部分发生了变化,但用户不一定跳到你应用程序的完全不同的部分。(例如:在 iTunes Store 应用程序中,点击歌曲旁边的价格标签(0.99 美元等)会将其更改为“购买”按钮。)此通知告诉 VoiceOver 重新读取所有可访问项目的当前状态显示在屏幕上,通过这样做,它会找出发生了什么变化并通知用户这些变化。它会提醒 VoiceOver 布局已更改,并且它的当前索引已过时,因为屏幕上的项目已自行重新排序。

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
于 2015-02-04T23:52:24.843 回答