I have implemented an in-line UIDatePicker
in a UITableView
just like they appear in other iOS apps, for example when creating a new event in Calendar.
While using the app with VoiceOver, the date picker is still accessible after the user selected a date and then closed the date picker by double tapping when on the table view cell. When the date picker hides, VoiceOver highlights the appropriate cell, but if the user then swipes to the right it will select the now hidden UIDatePicker
and allow them to interact with it. I expected it to select the next table view cell like it does in Calendar.
I have read on other SO questions that hidden views are still accessible, and in order to prevent that from occurring, you can set the property accessibilityElementsHidden
to YES
on the container view after you hide the element to let VoiceOver know it's no longer on screen. But this did not work for me. I also read you should post a layout change notification so VoiceOver knows to update to the current UI, but that too didn't work and the hidden view is still accessible.
How can I prevent my hidden UIDatePicker
from being accessible? Note that when the table loads, the hidden date picker is not accessible. So only after it appears then hides is it still accessible.
This is my code to hide the date picker when the user taps the cell:
self.datePicker.hidden = YES;
self.datePicker.alpha = 0.0f;
self.datePickerCell.accessibilityElementsHidden = YES;
self.datePickerCell.contentView.accessibilityElementsHidden = YES;
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
And in heightForRowAtIndexPath
the datePickerCell
height is changed to 0 upon hide. Note that the datePicker
is a subview
of the datePickerCell
.
I tried to replicate your scenario on my own, and came up with something that works well with accessibility. The most important decision in my opinion was to use an NSLayoutConstraint for the height of the Date Picker.
This is an overview of the Storyboard I designed:
And this is what I did in the ViewController:
@IBOutlet weak var pickerHeightConstraint: NSLayoutConstraint!
var showPicker = false
@IBOutlet weak var datePickerView: UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
pickerHeightConstraint.constant = 0
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
showPicker = !showPicker
datePickerView.alpha = showPicker ? 1.0 : 0.0
pickerHeightConstraint.constant = showPicker ? 224 : 0
tableView.beginUpdates()
tableView.endUpdates()
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 1 {
return pickerHeightConstraint.constant
}
return UITableViewAutomaticDimension
}
Note that I didn't had to mess around with UIAccessibility properties such as accessibilityElementsHidden
. Just use the constant property of the Layout Constraint to manipulate the date picker view element.
You can find a project where I tested this here. A Gif can be found in the README so you can quickly see a short demo - my reputation is not enough for more links.