如何防止 VoiceOver 自动滚动 UICollectionView?

How to prevent automatic scrolling of UICollectionView by VoiceOver?

我有一个水平滚动的 UICollectionView,上面有一个标题标签,下面有一个 UIPageControl。

UILabel
UICollectionView
UIPageControl

当我打开 VoiceOver 辅助功能并开始按顺序遍历屏幕时,collection 视图会自动滚动到开头或结尾。使页面突然跳转。例如,如果我使用页面控件滚动到第二页,然后返回 collection 视图,它会意外地显示和读取最后一页。由于我在辅助功能模式下使用页面控件进行导航,因此我想防止自动滚动。

我该如何预防或应对?

我发现了一个似乎描述相同问题的问题,但没有解决方法建议:iOS 8.4: Scroll view resets contentOffset with Voice Over enabled shortly after view appear

我在 iOS 13.4.1 iPhone 11 Pro

上遇到了

UIScrollViewDelegate.scrollViewDidScroll(_:)

触发自动滚动的辅助功能焦点的更改也会触发对 UIScrollViewDelegate 中 scrollViewDidScroll(_:) 的调用。用它来对抗自动滚动效果,f.i。通过设置 contentOffset 您喜欢的方式。

您可能需要检测滚动实际上是由辅助功能触发的,而不是用户拖动或捏合。 UIAccessibility.isVoiceOverRunningUIAccessibilityFocus.accessibilityElementDidBecomeFocused() 是你的朋友。请注意,更改 contentOffset(或 zoomScale 或任何需要的)可能会触发对 scrollViewDidScroll(_:) 的另一次调用,因此您需要防止无限递归。

使用@pommy 的建议,我能够fix my similar issue。在我正在处理的代码中,进行更改的最合适位置最终是 CalendarCollectionView.setContentOffset(_:animated:),其中 CalendarCollectionViewUICollectionView 子类。具体来说,它是一个 JTACMonthView 子类,但这与此答案无关。

从名字就可以看出我的用例:一次显示一个月的日历。未来和过去可能有好几个月的时间,但通常的用户关注点可能会从中间的某个地方开始。

与 OP 一样,我发现在启用 VoiceOver 的情况下从外部元素滑动到集合视图会导致焦点转到日历中的第一个日期,在我的例子中是 1951 年 1 月 1 日(Date.farPast,我相信。)一个有趣的旁白:切换控制导航不会导致相同的行为。

基本行为是 contentOffset 在集合视图滚动的维度中被设置为 0.0。在我的代码中,该方向保存在 style 中,并根据配置进行更改,但在大多数应用程序中它可能是固定的。

当启用 VoiceOver 时,我的代码只是阻止对 0.0 的任何偏移更改。这非常幼稚,并不适合所有应用程序,但给出了一个具体示例,希望对其他人有所帮助!

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    if shouldPreventAccessibilityFocusScrollback(for: contentOffset) {
        return
    }
    super.setContentOffset(contentOffset, animated: animated)
}

func shouldPreventAccessibilityFocusScrollback(for newContentOffset: CGPoint) -> Bool {
    if UIAccessibility.isVoiceOverRunning {
        switch style {
        case .horizontal:
            return newContentOffset.x == 0
        case .vertical:
            return newContentOffset.y == 0
        }
    }
    return false
}

我花了很长时间试图确定 UIAccessibilityFocus 何时从集合视图外的内容移动到集合视图内的内容,理想情况下,这是我们唯一想要阻止这些自动滚动的时间。我没有成功,但我认为这主要是由于子类化了第三方集合视图(日历)。如果您能让它发挥作用,那么这种方法肯定有更多优点……但它需要对状态进行一些仔细的管理。