VoiceOver reads accessibility label twice when focusing non-UILabel titleView

十年热恋 提交于 2021-02-10 06:18:37

问题


I'm encountering a strange issue with VoiceOver.

Goals:

  • Set a UIStackView containing multiple UILabel's as my navigationItem.titleView.
  • Mark the stack view as an accessibility element and set its accessibilityLabel to an appropriate value.
  • Set the stack view as the initial VoiceOver focus by calling UIAccessibility.post(notification: .screenChanged, argument: navigationItem.titleView) inside viewDidAppear(animated:).

Expected Result:

  • When the view controller appears the focus appears to be on the title view and VoiceOver reads the contents of the accessibility label one time.

Actual Result:

  • VoiceOver starts to read the contents of the accessibility label and then part way through (or sometimes after finishing) it proceeds to read it a second time.

This issue does not occur if I set navigationItem.titleView to an instance of UILabel.

Does anyone know why this happens? Is it a bug in iOS?

I have set up a simple project demonstrating the issue here: https://github.com/rzulkoski/Focus-TitleView-Bug


回答1:


The reason why you have a second time reading of your title is in your code.

In your viewDidLoad, you set the stackview accessibility label that VoiceOver automatically reads out to inform the user of the changing.

Next, you notify this changing with a post in your viewDidAppear that VoiceOver naturally reads out as well.

To prevent from this behavior, just delete stackView.accessibilityLabel = label.text in your setupNavigationItem function and add this snippet in your private lazy var label init :

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

Updating the stackView.accessibilityLabel this way doesn't trigger VoiceOver to inform the user and allows to get your purpose.

However, I don't recommend to read out the title as the first element of a new page unless you reorder the presented elements.

VoiceOver users won't naturally guess that another element is present before the title :

  • They may not find a way to get back to the previous page.
  • They may be lost if they get the first element of the page with a 4 fingers simple-tap because they'll get the back button and not the title.

Technically, your problem is solved with the piece of code above but, conceptually, I suggest to reorder your elements if you still want to expose the title as the first element.

==========

EDIT (workaround)

About the technical problem, you're right in your comment, this solution above works thanks to the label reading by VoiceOver.

I commited a solution in your git branch you gave in your initial post.

The problem deals with the UIStackView I cannot explain in this case and cannot solve neither as is.

To reach your purpose, I created a UIAccessibilityELement for the stackview that can be perfectly reached and exposed with no double reading with a postnotification.

I did that because I couldn't get programmatically the stackview new size when the labels are in... maybe creating a UIStackView subclass and get into its layoutSubviews could be the trick ?

This solution should work as a workaround but I don't know the reason why this behavior appears with a UIStackview.

==========

EDIT (solution)

The problem is the way the titleView of the navigationItem is created. The best way to achieve your purpose is to :

  • Initialize your titleView as a simple UIView whose frame is the same as the stackview's.
  • Add the stackview as a subview after having specified its frame and its accessibility properties.

Follow the steps hereafter in your code :

  • Add the .header trait in the stackview property :

    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
    }()
    
  • Change the stackview case in your 'switch...case...' code section as below :

    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)
    }
    

Now, the postNotification reads out your stackview only once as the first element of your screen.



来源:https://stackoverflow.com/questions/54954861/voiceover-reads-accessibility-label-twice-when-focusing-non-uilabel-titleview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!