How can I queue multiple accessibility notifications for VoiceOver?
What am I missing here?
I have made many tests to try and understand this behaviour that drove me crazy.
My conclusion is that if you send a notification while VoiceOver is speaking a {label / hint / value}, your notification won't be taken into account : there may be a kind of preemption when the system needs to vocalize an attribute of a focused element.
That's only at the end of the vocalization that you can post as many notifications as you want to be well analyzed and interpreted as you wish.
The UIAccessibilitySpeechAttributeQueueAnnouncement
key is only useful with your own notifications when the system doesn't need to take over.
If you send many notifications and that the user flicks to focus a new element for instance, the notifications that weren't vocalized will be removed as soon as the system vocalizes the element's attributes.
In that case, if you catch the UIAccessibilityAnnouncementDidFinish
event, you will have a false value with the UIAccessibilityAnnouncementKeyWasSuccessful
key for the last vocalized notification (UIAccessibilityAnnouncementKeyStringValue
)... all the following ones will be ignored with no info given by the observer.
Conclusion : no personal notification is taken into account by VoiceOver when a new focused element or screen/layout changed notification occurs.
How can I queue multiple accessibility notifications for VoiceOver?
According to what's exposed above, I suggest to set up a kind of retry mechanism that would still send your notifications (x times) while they're not perfectly received after y seconds for instance.
That could be a tricky way to be more certain that the notifications are flawlessly received.
How can I queue multiple accessibility notifications for VoiceOver?
What am I missing here?
I have made many tests to try and understand this behaviour that drove me crazy.
My conclusion is that if you send a notification while VoiceOver is speaking a {label / hint / value}, your notification won't be taken into account : there may be a kind of preemption when the system needs to vocalize an attribute of a focused element.
That's only at the end of the vocalization that you can post as many notifications as you want to be well analyzed and interpreted as you wish.
The UIAccessibilitySpeechAttributeQueueAnnouncement
key is only useful with your own notifications when the system doesn't need to take over.
If you send many notifications and that the user flicks to focus a new element for instance, the notifications that weren't vocalized will be removed as soon as the system vocalizes the element's attributes.
In that case, if you catch the UIAccessibilityAnnouncementDidFinish
event, you will have a false value with the UIAccessibilityAnnouncementKeyWasSuccessful
key for the last vocalized notification (UIAccessibilityAnnouncementKeyStringValue
)... all the following ones will be ignored with no info given by the observer.
Conclusion : no personal notification is taken into account by VoiceOver when a new focused element or screen/layout changed notification occurs.
How can I queue multiple accessibility notifications for VoiceOver?
According to what's exposed above, I suggest to set up a kind of retry mechanism that would still send your notifications (x times) while they're not perfectly received after y seconds for instance.
That could be a tricky way to be more certain that the notifications are flawlessly received.
Swift: Accessibility: How can I queue events to be executed in sequence?
After you post your first announcement you need to wait for UIAccessibilityAnnouncementDidFinishNotification
(see more here) before you post the 2nd one.
So build a queue (a set could do it) and whenever UIAccessibilityAnnouncementDidFinishNotification
is triggered by the system just pop the first notification in your set (if present) and fire it away.
Why is UIAccessibility.post(notification: .announcement, argument: arg) not announced in voice over?
I am able to get this to work using a retry mechanism where I register as an observer of the UIAccessibility.announcementDidFinishNotification
and then pull the announcement and success status out of the userInfo
dictionary.
If the success status is false and the announcement is the same as the one I just sent, I post the notification again. This happens on repeat until the announcement was successful.
There are obviously multiple problems with this approach including having to de-register, what happens if another object manages to post the same announcement (this shouldn't ever happen in practice but in theory it could), having to keep track of the last announcement sent, etc.
The code would look like:
private var _errors: [String] = []
private var _lastAnnouncement: String = ""
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(announcementFinished(_:)),
name: UIAccessibility.announcementDidFinishNotification,
object: nil
)
}
func showErrors() {
if !_errors.isEmpty {
view.errorLabel.text = _errors.first!
view.errorLabel.isHidden = false
if UIAccessibility.isVoiceOverRunning {
_lastAnnouncement = _errors.first!
UIAccessibility.post(notification: .announcement, argument: _errors.first!)
}
} else {
view.errorLabel.text = ""
view.errorLabel.isHidden = true
}
}
@objc func announcementFinished(_ sender: Notification) {
guard let announcement = sender.userInfo![UIAccessibility.announcementStringValueUserInfoKey] as? String else { return }
guard let success = sender.userInfo![UIAccessibility.announcementWasSuccessfulUserInfoKey] as? Bool else { return }
if !success && announcement == _lastAnnouncement {
_lastAnnouncement = _errors.first!
UIAccessibility.post(notification: .announcement, argument: _errors.first!)
}
}
The problem is that this retry mechanism will always be used because the first call to UIAccessibility.post(notification: .announcement, argument: _errors.first!)
always (unless I am stopped on a breakpoint). I still don't know why the first post always fails.
UIAccessibilityAnnouncementNotification asynch issue
For iOS 6.0+ you may use UIAccessibilityAnnouncementDidFinishNotification to synchronize your announcements.
Call iOS accessibility voice over programmatically on a UI element
If you want to present some important information to a Voice Over, then you should post an "accessibility announcement":
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, // announce
@"My important information"); // actual text
Voiceover has huge pause using UIAccessibilityPostNotification
If you can support only iOS 6 and forwards then you can use UIAccessibilityAnnouncementDidFinishNotification
to ensure that the announcement finished before continuing.
You would observe it like any other notification
// Observe announcementDidFinish to know when an announcment finishes
// and if it succuded or not.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(announcementFinished:)
name:UIAccessibilityAnnouncementDidFinishNotification
object:nil];
The notification you get back comes with the text of the announcement and if all the text was read or if the announcement was aborted. If you have multiple announcements then you can wait for the correct one.
// When an announcement finishes this will get called.
- (void)announcementFinished:(NSNotification *)notification {
// Get the text and if it succeded (read the entire thing) or not
NSString *announcment = notification.userInfo[UIAccessibilityAnnouncementKeyStringValue];
BOOL wasSuccessful = [notification.userInfo[UIAccessibilityAnnouncementKeyWasSuccessful] boolValue];
if (wasSuccessful) {
// The entire announcement was read, you can continue.
} else {
// The announcement was aborted by something else being read ...
// Decide what you want to do in this case.
}
}
Related Topics
How to Animate Dark Mode Change in iOS
How to Dismiss a Modal Segue Then Perform Push Segue to New View Controller
No Such Module Iqkeyboardmanagerswift
Are Private Frameworks Supported on iOS
How to Animate Uilabel's Textcolor Change
Default Uifont Size and Weight But Also Support Preferredfontfortextstyle
Uicollectionview Inside Tableviewcell Not Called
iOS Getting My Location Regularly and Setting Button to Get My Location - Google Map Not Working
How to Set Requestserializer in Alamofire
Activity Indicator in iOS Launch Screen Doesn't Animate
Ar Refernce Image Plane Was Not Position Properly in iOS Swift
Terminating App Due to Uncaught Exception 'Nsinvalidargumentexception' - iOS Google Sign In
Initialization of 'Unsafepointer<Int>' Results in a Dangling Pointer
How to Figure Out When a HTML5 Video Player Enters The Full Screen Mode on iOS/Ipads
Can You Listen to Firestore Updates When iOS App Is in The Background
Can Push Notifications Be Used to Run Code Without Notifying User
How to Write a Better Data Access Layer with Realm
Conforming to UItableviewdelegate and UItableviewdatasource in Swift