Combine UISwitch and UITableViewCell for VoiceOver interaction - ios

In Calendar, when you create a new event, if you tap on the All Day cell with VoiceOver enabled, Siri says "All Day switch button on/off, double tap to change setting". And indeed double tapping will toggle the switch. Also, it's not possible to tap on just the toggle switch itself - you have to interact with the cell itself to toggle the switch, the switch itself is not an accessible element.
In my app I have the exact same setup with a label and a switch. But when I tap the cell with VoiceOver enabled it only reads the label so the blind user has no idea there's a toggle switch in that cell. If they tap the switch itself then they can interact with it, so it's the opposite of the setup in the Calendar app.
How can I obtain the same behavior that Apple implemented? I need some way to combine the switch into the cell so VoiceOver reads both upon highlighting the cell, then when they double tap it should toggle the switch, and I'm not sure how that setup can be accomplished. Thanks!

To implement the desired behavior, instead of placing the UISwitch in the contentView of the cell, add it as the accessoryView programmatically. Then the cell and switch will behave exactly as expected when using VoiceOver, exactly as it does in Calendar.

You should be able to set a custom accessibility description on the cell using
cell.accessibilityLabel = #"Double tap to toggle setting";
You can set up custom gestures for when VoiceOver is running according to this answer:
https://stackoverflow.com/a/12337128/567511
But here you would not need custom gestures, instead your didSelectRowAtIndexPath would flip the switch only when UIAccessibilityIsVoiceOverRunning is true.

I would like to elaborate on the answer of Joey, and clarify how a solution to this question can be achieved in code.
In the tableView:cellForRowAtIndexPath:, create a custom UISwitch view and append it to the accessoryView of the cell. This could look something like the following.
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
[switchView setOn:NO];
[switchView addTarget:self action:#selector(selector:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchView;
return cell;
The cell will now behave like any native iOS switch known from e.g. Settings or Calendar. Double tapping on the cell with VoiceOver enabled, will now toggle the UISwitch (on/off), and VoiceOver will automatically notify the user about the state of the switch, together with an accessibility hint.

Related

iOS UITest : How to find UITableViewCell's AccessoryView?

Hello I'm learning UITests now
I have a question
how can detect accessoryView's tap on the tableViewCell?? in UITest
below is my tableViewCell
I want detect detail closure accesorry view's tap
like this
app.tables.cells.buttons["FTCellAccesoryIdentifier"].tap()
but accesorry is a subclass of UIView
so i allocate accisibility identifier key in cellForRows function
cell.accessoryView?.accessibilityIdentifier ="FTCellAccesoryIdentifier"
and my test function is
i try
app.tables.cells.elementBoundByIndex(lastCellIndex).otherElements.elementMatchingType(.Any, identifier: "FTCellAccesoryIdentifier").tap()
but not work
is possible to tap cell's accesoryView in UITests?
TL;DR
Regarding...
is possible to tap cell's accesoryView in UITests?
... Yes.
But you have to use the default accessibility identifier for that.
For example, for a cell like this one:
Tapping the Detail Disclosure button during a UI test ("More Info" is the key here):
XCUIApplication().tables.buttons["More Info, Title"].tap()
Tapping the cell itself during a UI test:
XCUIApplication().tables.staticTexts["Title"].tap()
Long story:
There is no way to change the default accessibility identifier of the Detail Disclosure. From Apple:
Note: If your table cells contain any of the standard table-view elements, such as the disclosure indicator, detail disclosure button,
or delete control, you do not have to do anything to make these
elements accessible. If, however, your table cells include other types
of controls, such as a switch or a slider, you need to make sure that
these elements are appropriately accessible.
At the time you try to set the accessibility identifier/label of the Detail Disclosure in cellForRow, cell.accessoryView is nil. Why? Again, from Apple:
accessoryView
Discussion
If the value of this property is not nil, the UITableViewCell class
uses the given view for the accessory view in the table view’s normal
(default) state; it ignores the value of the accessoryType property.
The provided accessory view can be a framework-provided control or
label or a custom view. The accessory view appears in the the right
side of the cell.
The accessory view will be nil until you define them. :/
You would need to provide your "own" Detail Disclosure (e.g.: a customized UIView) if you really want to set a custom identifier for it. For example (to illustrate):
cell.accessoryView = UIView(frame: CGRectMake(0, 0, 20, 20))
cell.accessoryView?.backgroundColor = UIColor.blueColor()
cell.accessoryView?.accessibilityIdentifier = "FTCellAccesoryIdentifier"
You should use accessoryButtonTappedForRowWithIndexPath delegate method to handle click on accessoryView.
Then by indexPath parameter of method you can know which cell it is!
Hope this will help :)

KIF cannot tap UIButton inside UITableViewCell if it's its UITableView's only one

I am having trouble automating tapping on an UIButton that is embedded inside a UITableViewCell if that cell is the table's only one. This is in the context of UI automation with KIF.
Here my relavant call:
[tester tapViewWithAccessibilityLabel: #"LABEL"
traits: UIAccessibilityTraitButton];
// trait only specified for 2nd case below
Here is what I am observing:
If I put the accessibility label on the UITableViewCell KIF's KIFUITestActor - waitForAccessibilityElement:view:withLabel:traits:tappable: returns the UITableView, not the cell. Somehow the table seems to inherit its only child's accessibility label and lets KIF encounter it first during its recursive search.
If I put the accessibility label on the UIButton instead, KIF finds it but determines that is is not tappable (i.e. UIView-KIFAdditions -tappablePointInRect: returns NO), presumably because its mainly transparent between the thin font lines for the button's label (the tap goes to a UITableViewCellContentView instead).
A workaround might be tapping on the row by it's NSIndexPath but maybe there is still a better way to overcome the described hurdles I am facing. So how could I instruct KIF to tap a button like this with a call to tapView...?
If you are making cell in code, make sure your button is added to cell.contentView.
If you are loading cell from xib, try to send contentView to back of view hierarchy (I have not found any downsides yet for doing it) with:
[self sendSubviewToBack:self.contentView];
This is just to confirm that this workaround is applicable:
[tester tapRowAtIndexPath: [NSIndexPath indexPathForRow: 0 inSection: 0]
inTableViewWithAccessibilityIdentifier: #"IDENTIFIER"];
The "good" solution should be something like this:
UITableViewCell *cell = [tester waitForCellAtIndexPath:indexPath inTableViewWithAccessibilityIdentifier:tableAccessibilityIdentifier];
UIAccessibilityElement *element = [cell accessibilityElementWithLabel:accessibilityLabel traits:UIAccessibilityTraitButton];
[tester tapAccessibilityElement:element inView:cell];
Unfortunately it doesn't work as expected. Sometimes the element is a UIAccessibilityElementMockView and tapping it in step 3 leads means just tapping the underlying cell. Naturally the results will not be as expected.
I've managed to finally work around that by doing something more along the lines of:
MyCustomCellClass *cell = (MyCustomCellClass *)[self waitForCellAtIndexPath:indexPath inTableViewWithAccessibilityIdentifier:tableAccessibilityIdentifier];
[cell.buttonOutlet sendActionsForControlEvents:UIControlEventTouchUpInside];
Note that this means
relying less on UI interaction
having a custom class
having an outlet to the button
But hey, KIF isn't supported by Apple so it sometimes, you know, breaks :)

iOS - Can I disable accessibility on cell.textLabel.text?

I have an app that contains a view with a cell that uses the built-in cell.textLabel and a custom UITextField in cell.contentView.
I am working with Voiceover and accessibility and the desired behavior would be that whenever I tap anywhere in the cell, the accessibility element for the UITextField would be selected.
The behavior that I am actually seeing is that the cell.textLabel accessibility labels are taking over. When I don't have cell.textLabel set to anything, everything works as expected. I have also attempted to set the "isAccessibilityElement" property with no luck:
[cell.textLabel.text setIsAccessibilityElement:NO];
Does anyone know how to make this work the way I want?
I was able to figure this out using this:
cell.textLabel.accessibilityElementsHidden = YES;

UIButton subview in UICollectionViewCell not changing control state

I have a UICollectionView where each UICollectionViewCell has a UIButton as a subview. The UIButtons respond to taps no problem (their targets get fired), but the button itself does not change to the selected state (no change in look and feel of the button). I have a hunch it's because of the UICollectionViewCell not properly forwarding its touch events to the button, but I'm not sure. Even if that's so, how do I set things up so that the button's state changes properly in this scenario?
The UIScrollview (and thus UICollectionView too) has a property called delaysContentTouches, by default it is set to YES, change this to NO and your button should highlight like it is supposed to.
If i may suggest an alternative, the UICollectionView has an awesome delegate method called
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { }
which could handle the click to that cell... if you are looking for specific events to happen like seeing an image change to the state of the button, you could hard code that in... when they press the button do one thing, when they release the button do another thing....
according to the documentation as well
UIControlStateSelected
Selected state of a control. For many controls, this state has no effect on behavior or appearance. But other subclasses (for example, the UISegmentedControl class) may have different appearance depending on their selected state. You can retrieve and set this value through the selected property.
in laymens terms.. for a UIButton the "Selected State" does nothing...
if the the button is suppose to dim when it's clicked and it's not doing that, then you may have to do that programmatically if, but i'm not exactly sure what you are trying to do...
the dimming feature is with in the highlighted state
UIControlStateHighlighted
Highlighted state of a control. A control enters this state when a touch enters and exits during tracking and when there is a touch up event. You can retrieve and set this value through the highlighted property.
in laymens terms, you touch the button it's highlighted
to see if the button is changing states properly you can do something like this
[button addTarget:self action:#selector(functionToCall:) forControlEvents:UIControlEventAllTouchEvents];
NSLog(#"Selected: %i", button.selected);
NSLog(#"Highlighted: %i", button.highlighted);
NSLog(#"Normal State or not: %i", button.state);
the "functionToCall will be called when any type of touch even happens to the button and with in that function you could have those 3 NSLogs which will print to your console the different UIControlState values, this will show that the button is working properly and show that it may be a UIViewCollection error, if it's the UICollectionView... then you will have to programmatically dim the button :3
hope this helps !

UIAutomation iOS - delete a table cell

I am working on deleting a table cell. I thought it will be done this way.
var myCell = cells["abc"];
var deleteSwitch = myCell.switches()[0];
deleteSwitch.tap();
but, when I tried to logElementTree in myCell, it did not show me any UIASwitch.
It displayed UIAStaticText which has the name of the cell and toggle button in order to enter edit mode. When I record my actions to capture script, I tried o tap on delete button to delete text and it recorded as a tap on that cell but not on switch/button.
Please let me know how to delete a cell.
In order for any element to appear in element tree, you should have accessibility enabled on that element. Do you have accessibility enabled on UISwitch inside iOS code? If your switch name in iOS code is say 'mySwitch', then try doing:
[mySwitch setIsAccessibilityElement:YES];

Resources