'Unable to set accessibilityIdentifier of UISegmentedControl's segments
I found out, that even though I could set accessibilityLabel
of UISegmentedControl
's segment (see: How do I set the accesibility label for a particular segment of a UISegmentedControl?), I couldn't set accessibilityIdentifier
, which was equally important for my project. I need to target a segment irrespective of its text and accessibilityLabel
for automation purposes.
For example, the code:
NSString *name = [NSString stringWithFormat:@"Item %li", (long)idx];
segment.accessibilityIdentifier = name;
NSLog(@"ID: %@", segment.accessibilityIdentifier);
results in:
ID: (null)
No exceptions are thrown.
Does anybody have insight into why accessibilityLabel
is implemented, but not accessibilityIdentifier
?
Solution 1:[1]
I got around this issue by writing a Swift extension for XCUIElement
that added a new method tap(at: UInt)
. This method gets the buttons
query of the element and sort the results based on their x position. This allows us to specify which segment of the UISegmentedControl
we want to tap rather than relying on the button text.
extension XCUIElement {
func tap(at index: UInt) {
guard buttons.count > 0 else { return }
var segments = (0..<buttons.count).map { buttons.element(boundBy: $0) }
segments.sort { $0.0.frame.origin.x < $0.1.frame.origin.x }
segments[Int(index)].tap()
}
}
Solution 2:[2]
Here is an example of looping through the views to set the accessibilityIdentifier by referencing the segment title.
Unfortunately when you set the identifier it doesn't persist. UISegment
s must be doing some tricky overriding. Still at a loss for how to get this to work.
extension UISegmentedControl {
/// Sets accessibility for segment buttons
func setAccessibilityIdentifier(_ accessibilityIdentifier: String, for segmentTitle: String) {
guard let segment = subviews.first(where: {
$0.subviews.contains(where: { ($0 as? UILabel)?.text == Optional(segmentTitle) })
}) else { return }
segment.accessibilityIdentifier = accessibilityIdentifier
}
}
Solution 3:[3]
I had tested the following code with Xcode 5.1.1 and iOS Simulator 7.1:
UISegmentedControl *contol = [[UISegmentedControl alloc] initWithItems:
@[@"0", @"1"]];
[self.view addSubview:contol];
UIView *segment = [[contol subviews] firstObject];
segment.accessibilityIdentifier = @"Item 0";
NSLog(@"ID: %@", segment.accessibilityIdentifier);
it didn't work for iPhone Retina (3.5-inch)
and iPhone Retina (4-inch)
i.e. result was:
ID: (null)
but it worked for iPhone Retina (4-inch 64-bit)
i.e. result was:
ID: Item 0
Then I've replaced @[@"0", @"1"]
with @[@"", @""]
in UISegmentedControl
initialization and the code worked for all mentioned platforms.
It appears, both accessibilityIdentifier
and accessibilityLabel
are implemented, but somehow the initial values of UISegmentedControl
interfere with accessibilityIdentifier
s of its segments.
Solution 4:[4]
I implemented this workaround and got automation to work (with KIF).
Code is in Swift, works for me with Xcode 6.1.1, iOS 8 SDK
for index in 0..<segmentedControl.numberOfSegments {
let identifierView = UIView()
identifierView.accessibilityIdentifier = "Item \(index)"
(segmentedControl.subviews[index] as UIView).addSubview(identifierView)
}
Solution 5:[5]
I only had images, without any labels, so I used the code below. I found the indexes didn't correspond to the order on screen, so I keyed off of the initial accessibilityLabel
values, which were the names of the images I specified in Interface Builder.
override func viewDidLoad() {
super.viewDidLoad()
for segment in segmentedControl.subviews {
switch segment.accessibilityLabel {
case .Some("First Image"):
segment.accessibilityLabel = "Description of first item"
break
case .Some("Second Image"):
segment.accessibilityLabel = "Description of second item"
break
default:
NSLog("Unknown accessibility label: \(segment.accessibilityLabel)")
break
}
}
}
Solution 6:[6]
I ran into this. Some of the previous answers didn't seem to address the accessibilityIdentifier at all. I did try Jawwad's approach of accessing the segments and adding a new UIView and setting the accessibilityIdentifier on that. However, I'm using EarlGrey for UI Testing and unfortunately, when it tried to tap on that view, it didn't work. However, based on this I did the following variation which DID work.
The trick is to enumerate the segments (as per Jawwad) and then for each, find the UIImageView subview and set its accessibilityID. This works for lookup and interaction.
let ids = ["Identifier1", "Identifier2"]
for index in 0..<segmentedControl.numberOfSegments {
if let view = p.subviews[index] as? UIView {
for v in view.subviews {
// Setting the ID twice would cause EarlGrey tests to fail
if let iv = v as? UIImageView, iv.accessibilityIdentifier == nil {
iv.accessibilityIdentifier = ids[index]
break
}
}
}
}
Solution 7:[7]
I was able to work around the issue by overriding UIView's NSArray *accessibilityElements property, and adding accessibilityIdentifier to each of the returned UISegment.
- (NSArray *)accessibilityElements {
NSArray *elements = [super accessibilityElements];
[elements enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
obj.accessibilityIdentifier = self.identifiers[idx].accessibilityIdentifier;
}];
return elements;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Daniel Wood |
Solution 2 | Patrick |
Solution 3 | |
Solution 4 | Jawwad |
Solution 5 | Dov |
Solution 6 | |
Solution 7 | Marcell Kresz |