I have a horizontal stackview with a button and label horizontally
When I turn on voice over, text in these buttons are not reading at all. All others works fine.
Below is how i setup my code. Please guide how to enable these. Not sure why this isn't working
I have my button text and label text taking dynamic values
Also if any info why the accessibilityIdentifier is used would really help. I couldn't find clear explanation for this
self.myHorizontalStackView.isAccessibilityElement = true
//Accessibility for Button
self.submitButton.isAccessibilityElement = true
self.submitButton.accessibilityTraits = UIAccessibilityTraitStaticText
self.submitButton.accessibilityLabel = screenControls.buttonTitle
//Accessibility for Label
self.addressLabel.isAccessibilityElement = true
self.addressLabel.accessibilityTraits = UIAccessibilityTraitStaticText
self.addressLabel.accessibilityLabel = screenControls.address1
So the problem looks like it's coming from your stackView itself. See this other SO question/answer on the subject:
If a container view is accessible, its child views are often obscured to avoid confusion about what the user is trying to interact with.
So here, change your first line in the snippet you shared to:
self.myHorizontalStackView.isAccessibilityElement = false
and that should enable VoiceOver to find the subviews of the stackView.
As far as your other question:
Also if any info why the accessibilityIdentifier is used would really help. I couldn't find clear explanation for this
If you are asking what the identifier would be used for, it's primarily for testing purposes. By setting unique identifiers to your views, your tests can easily use the accessibility engine to find specific views and perform any actions you'd want to test. In terms of VoiceOver, the accessibilityIdentifier does not have a role.
Related
Guys just cant figure out how to tap on BackButtonItem from Navigation Bar with Calabash framework, I'm setting accessibilityLabel like this:
self.navigationItem.leftBarButtonItem?.accessibilityLabel = "goBack"
and trying to test it like this with no luck:
touch("* marked:'goBack'")
touch_transition('navigationItemButtonView first',
"* marked:'#{goBack}'")
The problem is that UINavigationItem, UITabBarItem, and UIToobarItem are converted, at runtime, to Views. For example, a UITabBarItem is converted to a UIToobarButton. In the conversion, the accessibilityIdentifier and accessibilityLabel are not preserved.
# This will probably get you the left navbar button
query("UINavigationItemButtonView index:0")
There are ways to enforce that an accessibilityIdentifier is preserved, such as making the navigation item from a custom view.
Take a look at the briar bars/navbar.rb for inspiration. I do not recommend using briar in your project; its life is uncertain (I am the maintainer).
I'd recommend trying query "all * marked:'goBack'" and if that still doesn't return any results, just try a query "all *" and see if the label shows up in the results.
You could also try setting the accessibilityIdentifier of the view - generally that's the preferred way to set up views for automation.
Just updating it might be helpful for someone who wish to use default back button
touch('navigationItemButtonView first')
This will take you back.
Found from calabash predefined steps.
I ran into a weird situation just now. I have a container view in our app which needs to read out all of the views when tapped, but only one view should be able to be accessed individually. Because some of the views are complex views with their own subviews, I setup my ADA logic to read the accessibilityLabel of a view if it has one, and if not, to build one from the subviews.
Well, today we had to add the word "button" to the help button when it's being read because the container view was tapped. So I simply added the word "button" to the label's accessibilityLabel and everything worked fine. Then I realized, this may change it to read "more information button button" when tapped individually, since I had set the accessibilityTraits property of the button to be UIAccessibilityTraitButton.
However, to my surprise, it read it correctly as "more information button." Does Apple detect extraneous "button's" in an accessibilityLabel and remove them when the trait is set to be a button? Would they also remove extraneous "label's" from a button when it's trait is set to be a label? I find this extremely cool, and incredible forethought on Apple's engineer's parts, if this is the way it works.
Edit: If this is the way it's done, could someone point me to where this is documented? I wasn't able to find it, although I'm currently still working on this fix so I didn't do the most in depth search I could have done.
I've had some time now to do some proper testing. VoiceOver will remove the extra labels if you set the traits properly. This allows for you to have the type read in a container view, and still set an accessibilityTraits.
So, for example, if you set the accessibilityTraits to UIAccessibilityTraitButton and the last word in your accessibilityLabel is button, it will remove the last word and only read button once. If, however, you set the accessibilityTraits to `UIAccessibilityTraitImage', it will not remove the button, and will end its reading with "button image". Changing the last word to "image", however, has it only read image once.
Pretty nice forethought on Apple's part with that implementation.
I am having trouble locating XCUIElements on a screen for the app I am testing. I realize you can access a button for example via something like:
app.buttons[].elementBoundByIndex(0)
But the problem is sometimes, the component is not found. Like in a case where I have a Button in a cell in a UITableView. I try to make an XCUIElementQuery to find the button, and it is not there. I try to look for tables or tableviews or collection views and even though they are in the view controller, they are not found in UI Testing. The count of the returned array will be zero.
I attempted originally to record the test, but clicking the element I am trying to access did not work. Xcode detected it as an "Other Element" and when trying to tap during, playback the application does not advance.
Is there a high level way to access a component like a UIView high in the UI hierarchy to cascade down?
I didn't know this at the time, but what I was looking for was basically debugDescription:
Set a breakpoint when you hit the area you're trying to debug. Then
po print(app.debugDescription)
in the debug console. You will see the hierarchy then.
Ideally you should set an accessibilityIdentifier on your button and search for it via that. The accessibilityIdentifier should be unique for elements on the screen. You can set an accessibilityIdentifier in the Identity Inspector in Interface Builder (command-option-3) or in code directly. Once you have one, the query looks like:
app.buttons["SomeAccessibilityIdentifier"]
Ryan Poolos answer was the best answer for me as it solved my issue with nested UI Elements
We had to solve it by removing a few accessibility identifiers on superviews in the stack. Not ideal but did get it working without changing actual functionality. – Ryan Poolos
So with that in mind, I found the xib file with the element in question, selected the element, selected the tab "Show the Identity Inspector" on the right panel and unchecked the Accessibility checkbox.
I then did a recording of the element which resulted in:
[[[[[XCUIApplication alloc] init].scrollViews.tables childrenMatchingType:XCUIElementTypeOther] elementBoundByIndex:index] childrenMatchingType:XCUIElementTypeButton].element;
Notice how XCUIElementTypeOther is in the query. This was not the case when the accessibility identifiers were enabled as I would get:
[[[[[XCUIApplication alloc] init].scrollViews.tables.otherElements containingType:XCUIElementTypeStaticText identifier:#"username"] childrenMatchingType:XCUIElementTypeButton].element tap];
Obviously I wouldnt know the username as it would always change and this was a major problem. But after removing the accessibility identifiers,
containingType:XCUIElementTypeStaticText identifier:#"username"
changed to
childrenMatchingType:XCUIElementTypeOther] elementBoundByIndex:index
SUCCESS :)
In my own experience you being able to find an element (an UIView in your case as UIButton inherits from UIview) through its accessibilityIdentifier depends on how you added it to the view.
I assume that in your case you added the button to the cell programatically with addSubView. If that's the case, probably you will not be able to access to it. I've had the same problem and asked the question here but no proper solution at the moment, so my advice is try to avoid adding views with addSubView the moment...
There's a "Containers" rotor option in Voiceover which allows the user to quickly navigate through "high level" sections of the screen via single finger swipe up and swipe down actions. For example, the Calendar app has three high level items: navbar, contents and toolbar.
My app uses custom UIView subclasses and, no matter what I try to do, all my views seem to belong to a single container. I can't split them into logical sections. I tried putting them in separate views implementing the UIAccessibilityContainer protocol and setting a few of the accessibility properties on the parent views.
Does anyone know how to create multiple containers?
I did some digging on this issue and think its a private trait Apple is using. First I noticed the only containers recognized are standard UIKit type objects like UITableViews, UITabBars, UINavigationBars, etc. So next I used the debugger to inspect the value of the accessibility traits for these components. They're all 0x200000000000. Just to be sure I didn't miss an UIAccessibilityTrait I checked all of their values. None of them match the value. Furthermore if you set your views accessibility traits to this mysterious value it'll work just like you want! I tried determining the location of this constant but didn't have much luck. If you want to do more digging it looks like apple stores accessibilityTraits using an NSObject category that uses associated objects with some constant value named AXTraitsIdentifier.
Practically speaking you could do something like the below but since its not defined in a public API its functionality could change in the future
//Note the navBar has to be run through a voice over pass before the value is set :( or you can just directly set the value to 0x200000000000.
myContainerView.accessibilityTraits = navBar.accessibilityTraits;
I'd love to hear if anyone one else has info on this? So far I haven't found an ideal solution.
I have been able to make the views in my app reachable by single finger swipe up and swipe down actions when the "Containers" rotor option is selected by setting the accessibilityContainerType property of my views to semanticGroup.
I say 'hyperlink' because I don't know what else to call it, and that is how i'd like it to appear.
Obviously this is possible using a combination of labels and buttons, but my labels and buttons are programmatically generated and I imagine i'd have to also programmatically arrange them, which would likely be tedious and inflexible in terms of changing font sizes etc.
Any ideas/approaches would be much appreciated!
As an example, look at Instagram's following and news feed:
You should set userInteractionEnabled and then add a UITapGestureRecognizer to the label.
Have a look at Nimbus Attributed Label it can provide the functionality you are looking for.