Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setting view.accessibilityElements with embedded view controllers

I am trying to add iOS accessibility support/Voice Over to my app. My main screen has three main controls, but the third control is hosted within an embedded view controller.

I am setting accessibility elements in prepareForSegue and have confirmed that the embedded view controller controls are all loaded. Problem is I can still only select the first two controls which are in the enclosing view controller.

self.view.accessibilityElements = 
    @[ 
        self.cmdMenu,                      // works
        self.collectionView,               // works
        self.childViewController.peerMenu  // doesn't work
    ];

All three views have isAccessibilityElement = YES.

Am I missing something? I can't imagine that there is a restriction on the accessibility elements being in the same view controller.

like image 688
Dan Loughney Avatar asked May 31 '16 15:05

Dan Loughney


1 Answers

I found my bug and now have Voice Over working. In the process I figured out a number of things that I would like to share.

  1. To my original question, you can reference controls in your child view controllers from your main view controller. You can add the controls directly (as I did in my question) or you can add all accessibility elements in the child view controller using self.view.accessibilityElements = @[ _control1, childViewController.view, childViewController2.view].
  2. If you add all of the controls in your child view controller as in (1.) then ensure that childViewController.view.isAccessibilityElement = NO.
  3. You can add any kind of object to accessibilityElements, even elements that have no accessibility information. The API will not assert or warn you. This ended up being my bug.
  4. If your UI changes and you need to change the number or order of items in your accessibilityElements array tell UIKit about it using UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self). The notification argument (where I'm sending self) tells Voice Over where it should position its cursor when the notification completes.
  5. If you want to read aloud some text for a transient notification (imagine when Clash Of Clans tells you how many Gems you found in that Tree Stump) call UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, messageText). One caveat, this won't read aloud the messageText unless there is no other Voice Over in progress. You need to manage the timing yourself. Submitted a bug on this. Apple could make this a lot better.
  6. If you are using UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, messageText) you can listen for UIAccessibilityAnnouncementDidFinishNotification, but unfortunately this notification has almost no value. You only will get notified if your messageText was fully spoken. It doesn't tell you that it was spoken, but interrupted, and it will also not get triggered for any text spoken through the UIKit framework.
  7. The Accessibility Inspector in the iOS Simulator kind of sucks. If your accessibility settings are correct, it can tell you what is there. If you have a problem the Inspector does not provide you any information about what is wrong. This is true of the entire UIAccessibility API. It is so easy to use that it almost always works. But when it doesn't work you need to resort to hunt and peck to figure it out. The API needs some assertions or console messages similar to how Apple handles Constraint warnings. Spoiler alert: the Accessibility Inspector in Xcode 8 is wayyyyy better, but still would not have helped with my issue.
  8. There is a ton of good information in the UIAccessibility.h header. If you are embarking on UIAccessibility support, it is a good read.
like image 67
Dan Loughney Avatar answered Nov 15 '22 18:11

Dan Loughney