iosiphoneaccessibilityvoiceoveruiaccessibility-notification

Voiceover has huge pause using UIAccessibilityPostNotification


I am adding accessibility to my iPhone game and make extensive use of UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, @"string") to announce various things happening in the game. It works well in 99% of time, but I am having one problem.

In all cases, voiceover announcements are performed from a single method I added to the application delegate.

- (void)voiceoverAction:(NSString *)speakString delay:(NSTimeInterval) delay {
    if (![[[[UIDevice currentDevice] systemVersion] substringToIndex:1] isEqualToString:@"3"]) {
        if (UIAccessibilityIsVoiceOverRunning()) {
            UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, speakString);
            if (delay > 0) {
                [NSThread sleepForTimeInterval:delay];
            }
        }
    }
}

The delay is there so the announcement is spoken before the next event happens in the game. I could not find a better way to ensure the whole announcement was spoken before some animation or other event cut it off.

In every case but one the announcement is spoken immediately when this method is called. In one case there is an approximate 10 second pause before the speaking is performed. In this case even if I debug the code and set a breakpoint and execute the UIAccessibilityPostNotification line manually the line executes but nothing happens. Then 10 seconds later, without doing anything in the debugger, the iPhone just starts speaking the announcement.

The only thing that is special about this one announcement is that it is called from a touchesEnded: event of a UIScrollView. Other announcements are part of the overall game loop and are not based on touch events.

Any idea what might cause voiceover to queue up an accessibility notification and not immediately speak it?

Thanks in advance, Steve


Solution

  • 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. 
        } 
    }