Is it possible to change the order in which the VoiceOver feature for accessibility in iPad reads out the elements, when the 'Two-finger Flick Down' gesture is done?
For the attached image, which contains 3 labels and a button, the VoiceOver reads the elements in the following way,
Label 1 -> Label 2 -> Button -> Label 3
Can the order be changed to,
Label 1 -> Label 2 -> Label 3 -> Button
The quickest way to achieve this for your example is to place the three labels in a transparent UIView subclass to serve as a container for your labels. This subclass will have to be properly setup to let VoiceOver know how to interpret it. If your deployment target is iOS6 then you can simply answer the "should group accessibility children" question in this subclass.
-(BOOL)shouldGroupAccessibilityChildren{
return YES;
}
For below iOS6 it would be more complicated, except that your UIView container subclass would contain only UILabels which are accessibility elements. You could implement it like this:
-(BOOL)isAccessibilityElement{
return NO;
}
-(NSInteger)accessibilityElementCount{
return self.subviews.count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
return [self.subviews objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
return [self.subviews indexOfObject:element];
}
I have tested this example code and it does what you are looking for, if you need any clarification please add a comment. Always happy to help make things more accessible.
I tried setting the shouldGroupAccessibilityChildren to YES but it didn't work for me.
What did work for me was setting the accessibility label of the parent view directly (because I wanted all the items to be read in one go/one VoiceOver gesture).
[cell setAccessibilityLabel:[NSString stringWithFormat:#"%#, %#", cityLabel, temperatureLabel]];
The above snippet of codes is from Apple's documentation Enhancing the Accessibility of Table View Cells
In Swift, attaching an IBOutlet to the parent UIView, then setting shouldGroupAccessibilityChildren to true will suffice.
abc.shouldGroupAccessibilityChildren = true
I did note that if also setting isAccessibilityElement = true the grouping will not take effect. Similarly, checking the accessibility checkbox in the storyboard or xib will also prevent the grouping from taking place.
I think you can do it in the storyboard. The VoiceOver order is determined by the order of the views in the document outline.
Just drag and drop the views in the view hierarchy in the right order.
Related
I have a UITableView with cells based on an XIB with UIStackViews containing UILabels, in it. Labels have the texts A,B,C,D
(look at screenshot).
When I enable VoiceOver or use the Accesibility Inspector, It automaticallly groups the labels in a cell, reading all the texts on the labels - that's fine.
But I want it to change the order that it reads the sub-labels, when the superview is focused, so it's read like A,B,C,D - currently it's A,C,B,D.
I've tried a lot of things with no luck, like overriding the accesibilityElements, shouldGroup... and isAccessiblityElement. Every time I change something, i get either:
No change
Empty accesiblity label for the cell
The single labels become selectable (which is not what I wanted)
Any tips on how to fix this one? I guess I can't be the only one in the world with this problem, but apparently it's hard to find any info on recent iOS versions. I use 10.3 and Swift 3...
I gave up on the idea of profiting form iOS own concatenation of the subviews' accesibility labels and made a piece of code to handle it myself.
extension UIView {
open func updateCombinedAccessibilityLabel (elements: [AnyObject]) {
let accLabels: [String] = elements.map { $0.accessibilityLabel ?? "" }
accessibilityLabel = accLabels.joined(separator: ", ")
}
}
Then, when I set the labels' text values, I tell the view how to arrange the subviews' accesiblity order:
view.updateCombinedAccessibilityLabel(elements: [view.titleLabel, view.subtitleLabel, view.detailsLabel])
Is it possible to change the order in which the VoiceOver feature for accessibility in iPad reads out the elements, when the 'Two-finger Flick Down' gesture is done?
For the attached image, which contains 3 labels and a button, the VoiceOver reads the elements in the following way,
Label 1 -> Label 2 -> Button -> Label 3
Can the order be changed to,
Label 1 -> Label 2 -> Label 3 -> Button
The quickest way to achieve this for your example is to place the three labels in a transparent UIView subclass to serve as a container for your labels. This subclass will have to be properly setup to let VoiceOver know how to interpret it. If your deployment target is iOS6 then you can simply answer the "should group accessibility children" question in this subclass.
-(BOOL)shouldGroupAccessibilityChildren{
return YES;
}
For below iOS6 it would be more complicated, except that your UIView container subclass would contain only UILabels which are accessibility elements. You could implement it like this:
-(BOOL)isAccessibilityElement{
return NO;
}
-(NSInteger)accessibilityElementCount{
return self.subviews.count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
return [self.subviews objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
return [self.subviews indexOfObject:element];
}
I have tested this example code and it does what you are looking for, if you need any clarification please add a comment. Always happy to help make things more accessible.
I tried setting the shouldGroupAccessibilityChildren to YES but it didn't work for me.
What did work for me was setting the accessibility label of the parent view directly (because I wanted all the items to be read in one go/one VoiceOver gesture).
[cell setAccessibilityLabel:[NSString stringWithFormat:#"%#, %#", cityLabel, temperatureLabel]];
The above snippet of codes is from Apple's documentation Enhancing the Accessibility of Table View Cells
In Swift, attaching an IBOutlet to the parent UIView, then setting shouldGroupAccessibilityChildren to true will suffice.
abc.shouldGroupAccessibilityChildren = true
I did note that if also setting isAccessibilityElement = true the grouping will not take effect. Similarly, checking the accessibility checkbox in the storyboard or xib will also prevent the grouping from taking place.
I think you can do it in the storyboard. The VoiceOver order is determined by the order of the views in the document outline.
Just drag and drop the views in the view hierarchy in the right order.
So I've created a ViewControl in my storyboard that has 3 subviews. Each one represents a different view I want to show depending on which table row was selected on the previous screen (NavControl). I start with all of the subviews hidden via the Attributes Inspector's 'hidden' attribute being checked. All of the objects within each of these views are NOT hidden, but are being hidden because the subview itself is hidden (obviously). Thinking I could use the tag attribute I've given each of the three subviews a tag (0, 1 and 2), but can't figure out how to use that either (just in case this is useful as providing me with an option of how to do this I wanted to mention it here).
So, how the heck do I show and then hide any of these subviews? I don't want to go through each object in a subview and toggle its hidden property to true/false I feel like I should just be able to 'show/hide' the entire subview. thus achieving the same result, but much more succinctly.
I can't find anything that will help me via web searches or stackoverflow searches.
My code is very simple. I capture the row that was selected in the previous screen and pass it to a variable on the details screen that contains the subviews. I know this is working because I've set up println()'s on the details screens viewDidLoad function. So now all I have to do is going into each of these conditions and tell it which subview to show and/or hide.
Thanks I appreciate all of this communities help! I'd be lost without it.
Use this to hide a view in swift
viewVar.isHidden = true
You should create IBOutlets for each of the three subviews. Then you can show/hide each of them directly from those references. If you hide a view, it will automatically hide its subviews.
Once you have an outlet for the view, you can do this:
viewYouWantToHide.isHidden = true
If you have tags for each view you can hide and display them using:
Objective C
For Hiding:
[[self.view viewWithTag:1] setHidden:YES];
Showing:
[[self.view viewWithTag:1] setHidden:NO];
In Swift:
Hiding:
self.view.viewWithTag(1)?.isHidden = true
Showing:
self.view.viewWithTag(1)?.isHidden = false
NOTE: Replace 1 with your tag value.
however the fact that isHidden is a naming convention for checking the status and is a getter method
but despite that fact in swift we use it as setter and getter property
view.isHidden = true
When the user has Voice Over on in certain apps, a one handed swipe to the right or the left changes the focused accessibility element and speaks it (for example, the App Store top charts view). I would like to have this in my own app (which uses a storyboard).
I can think of several ways to do this myself with a swipe gesture recognizer and a list of accessibility elements in order, but it seems like there must be a way to do this in the accessibility API. However, my research has turned up nothing.
Is this a built in feature? If so, how can I add it in my storyboard or in code?
Edit:
Per advice from one of the answer I have implemented the UIAccessibility protocol for my view.Here is the code.
- (NSInteger)accessibilityElementCount{
return 4;
}
- (id)accessibilityElementAtIndex:(NSInteger)index{
return [#[self.menuButton, self.firstButton, self.secondButton, self.thirdButton] objectAtIndex:index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element{
return [#[self.menuButton, self.firstButton, self.secondButton, self.thirdButton] indexOfObject:element];
}
The view I am having this issue with is defined in an interface builder storyboard. As you can no doubt infer from the code, it has 3 buttons as subviews.
What you are describing is the built-in behavior for VoiceOver and can't be changed on a per-app basis.
If you want to modify the order elements are focused, look at the UIAccessibilityContainer protocol for iOS 7 or accessibilityElements property of NSObject for iOS 8. If you don't want to implement either of those, you can also simply set accessibilityElementsHidden to YES for elements you want VoiceOver to ignore.
I have fixed the problem by adding accessibility labels to the buttons in the storyboard. Because voice over already spoke their label correctly, I had not bothered to do so before.
A single swipe gesture is intended by Apple to move the VoiceOver cursor through screen items in order but mine don't because the user can move them around!
My main view has a set of buttons and labels, however it also has two collections of custom subviews, let's call the instances SVA1 to SVA9 & SVB1 to SVB9 in 'ascending order' from left to right. That is, SVA is one custom UIView class and SVB is the 2nd. When I drag, say, SVA3 to the current position of SVA6 then I end up with an order of SVA1, SVA2, SVA4, SVA5, SVA3, SVA6, SVA7, SVA8 & SVA9. The collections are 'linked' so that that same order would now also be mirrored in the SVB subview collection via my code.
My problem is that swiping to the right, expecting VoiceOver to read out my items as I see them on screen results in a different order. It gets quite a lot of them right but then will suddenly move the VoiceOver cursor from the first collection to the 2nd or change its direction. After a move my code is aware of the new order of all the subviews but I'd like to be able to get that information to VoiceOver.
Each custom subview is an accessibilityElement. Is there a way that I can tell VoiceOver to read back my items in the order I'd expect? I've come across the -accessibilityElementAtIndex: method but don't see whether or how that fits with my situation.
Thank you.
If you want to change the order then accessibilityElementAtIndex: and the rest of the UIAccessibilityContainer protocol is what you are looking for.
Assuming that you have an array called accessibleElements that store the elements in the order you want them to appear.
- (NSInteger)accessibilityElementCount {
return self.accessibleElements.count;
}
- (id)accessibilityElementAtIndex:(NSInteger)index {
return self.accessibleElements[index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element {
return [self.accessibleElements indexOfObject:element];
}
The container can't be a accessibility element itself so you should also override isAccessibilityElement
- (BOOL)isAccessibilityElement {
return NO;
}