草庐IT

ios - 聚焦非 UILabel titleView 时,VoiceOver 会读取辅助功能标签两次

coder 2024-01-22 原文

我在使用 VoiceOver 时遇到了一个奇怪的问题。

目标:

  • 设置一个包含多个 UILabelUIStackView 作为我的 navigationItem.titleView
  • 将堆栈 View 标记为可访问性元素并将其 accessibilityLabel 设置为适当的值。
  • 通过在 viewDidAppear(animated:) 中调用 UIAccessibility.post(notification: .screenChanged, argument: navigationItem.titleView) 将堆栈 View 设置为初始 VoiceOver 焦点。

预期结果:

  • 当 View Controller 出现时,焦点似乎位于标题 View 上,并且 VoiceOver 会读取辅助功能标签的内容一次。

实际结果:

  • VoiceOver 开始朗读无障碍标签的内容,然后中途(或有时在读完之后)继续朗读第二遍。

如果我将 navigationItem.titleView 设置为 UILabel 的实例,则不会发生此问题。

有人知道为什么会这样吗?这是 iOS 中的错误吗?

我在这里建立了一个简单的项目来演示这个问题: https://github.com/rzulkoski/Focus-TitleView-Bug

最佳答案

你的标题有二次阅读的原因在你的代码中。

在您的 viewDidLoad 中,您设置了 VoiceOver 自动读出的堆栈 View 可访问性标签,以通知用户更改。

接下来,您通过 viewDidAppear 中的帖子通知此更改,VoiceOver 自然也会读出。

要防止这种行为,只需删除 setupNavigationItem 函数中的 stackView.accessibilityLabel = label.text 并将此代码段添加到您的私有(private)惰性变量标签 init 中:

if (self.view.subviews.contains(stackView)) {
        stackView.accessibilityLabel = label.text
}

以这种方式更新 stackView.accessibilityLabel 不会触发 VoiceOver 来通知用户并允许实现您的目的。

但是,我不建议将标题读出作为新页面的第一个元素,除非你 reorder呈现的元素。

VoiceOver 用户自然不会猜到标题前有另一个元素:

  • 他们可能找不到返回上一页的方法。
  • 如果他们获得带有 4 fingers simple-tap 的页面的第一个元素,他们可能会丢失因为他们会得到后退按钮而不是标题。

从技术上讲,您的问题已通过上面的代码解决,但从概念上讲,如果您仍想将标题显示为第一个元素,我建议您对元素重新排序。

==========

编辑(解决方法)

关于技术问题,您的评论是正确的,由于 VoiceOver 的标签阅读,上述解决方案有效。

我在您在初始帖子中提供的 git 分支中提交了一个解决方案。

问题涉及 UIStackView,在这种情况下我无法解释,也无法按原样解决。

为了达到您的目的,我为 stackview 创建了一个 UIAccessibilityELement,它可以完美地访问和公开,无需通过后通知进行双重读取。

我这样做是因为当标签位于时我无法以编程方式获得 stackview 的新大小...也许创建一个 UIStackView 子类并进入其 layoutSubviews 可能是诀窍?

此解决方案应该作为变通方法但我不知道此行为出现在 UIStackview 中的原因。

==========

编辑(解决方案)

问题在于 navigationItemtitleView 的创建方式。实现目标的最佳方式是:

  • 将您的 titleView 初始化为一个简单的 UIView,其框架与 stackview 的框架相同。
  • 在指定其框架及其可访问性属性后,将堆栈 View 添加为 subview 。

在您的代码中执行以下步骤:

  • 在 stackview 属性中添加 .header 特征:

    private lazy var stackView: UIStackView = {
        let stackView = UIStackView(frame: .zero)
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.distribution = .equalSpacing
        stackView.isAccessibilityElement = true
        stackView.accessibilityTraits = .header
        return stackView
    }()
    
  • 如下更改“switch...case...”代码部分中的 stackview case:

    case .stackView:
        label.text = "UIStackView"
        label.sizeToFit()
        stackView.addArrangedSubview(label)
    
        label2.text = subtitle
        label2.sizeToFit()
        stackView.addArrangedSubview(label2)
    
        stackView.frame.size.width = max(label.frame.width, label2.frame.width)
        stackView.frame.size.height = label.frame.height + label2.frame.height
    
        stackView.accessibilityLabel = label.text?.appending(", \(label2.text!)")
    
        navigationItem.titleView = UIView(frame: stackView.frame)
        navigationItem.titleView?.addSubview(stackView)
    }
    

现在,postNotification 只读出一次堆栈 View 作为屏幕的第一个元素。

关于ios - 聚焦非 UILabel titleView 时,VoiceOver 会读取辅助功能标签两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54954861/

有关ios - 聚焦非 UILabel titleView 时,VoiceOver 会读取辅助功能标签两次的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 在院子里用@param 标签警告 - 2

    我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?

  3. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  4. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  5. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  6. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  7. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  8. css - 用 watir 检查标签类? - 2

    我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes

  9. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  10. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

随机推荐