Making Complex UI WCAG-Compliant in Xamarin iOS Apps

 | 

What is WCAG?

According to wikipedia:

“The Web Content Accessibility Guidelines (WCAG) are part of a series of web accessibility guidelines published by the Web Accessibility Initiative (WAI) of the World Wide Web Consortium (W3C), the main international standards organization for the Internet. They are a set of recommendations for making Web content more accessible, primarily for people with disabilities—but also for all user agents, including highly limited devices, such as mobile phones. WCAG 2.0, was published in December 2008 and became an ISO standard, ISO/IEC 40500:2012 in October 2012. WCAG 2.1 became a W3C Recommendation in June 2018.”

WCAG Standards since their initial ratification have expanded to include how to develop mobile applications that adhere to accessibility standards that make an app’s user interface work seamlessly with a mobile OS’s built-in Accessibility features such as iOS’s VoiceOver accessibility feature.

Livongo and myStrength build programs to help people with a number of different chronic conditions. We feel that it’s very important to make those available to people with all levels of visual and physical capabilities.

iOS Accessibility and VoiceOver

VoiceOver is a very popular tool for giving those with vision impairment the ability fo fully navigate iOS applications with multi-touch.

Pictured BelowiOS VoiceOver Preferences

Pictured Below: Livongo MyStrength App running with VoiceOver enabled. 

As a result, the items with a black rounded rectangle are the items spoken aloud to the user.

What does VoiceOver mean for developing an accessible iOS app?

  • VoiceOver in general requires little configuration from the developer end. Other than setting your UI Element’s Accessibility Identifier string property, most of the time VoiceOver will work seamlessly as-is across many apps. The Accessibility Identifier is a string property that VoiceOver will speak out loud to the user when they tap that specific UI element. As a result for the above screenshot, VoiceOver will speak “Enjoy Your Morning” upon this element being tapped.
  •   Example code of Accessibility Identifier being set (C# in Xamarin.iOS):
  • In general VoiceOver is smart in being able to detect all of a developer’s elements on the screen to determine where the black rectangle should highlight and what should be spoken aloud to the user. However, as UI’s View hierarchies can become more complex, and you can have multiple views within a view, VoiceOver does not always accurately detect how many sub-items are in a View Collection that should be narrate-able or selectable.

Problem: VoiceOver doesn’t perfectly detect all navigable elements in some complex view hierarchies

Scenario: iOS Table Views where the Table View’s own cells can have a Collection View inside a Table View cell which then in turn, the Collection View has its own cells.

In the above example with the Livongo MyStrength app, the views are structured in the following way: 

In this case, VoiceOver can detect most of the UI Elements that are within the Viewport, however looking at the ‘Carousel’ View which is constructed by having a CollectionView inside a Table View Cell. Where the Table View Cell represents ‘Popular Activities’ and then contains a Collection View where each one of it’s cells represents a thumbnail view representing each activity.

There are 2 currently visible within the Viewport/visible screen area, however small slice of the third activity can be seen. Swiping left on the screen will reveal the remaining. However, iOS will only detect the first 3 items that are part of the initial Viewport. As a result when scrolling through the CollectionView’s items with VoiceOver, a user can scroll past the first 3 items, but then VoiceOver will jump down to the next cell in the Parent Table View. This limitation makes it impossible for a VoiceOver user to scroll horizontally to all the items in ‘Popular Activities’.

Pictured Below : Swiping left with VoiceOver 2 times (The next swipe should highlight the 4th item)

Pictured Below : Swiping Left with VoiceOver 3 times (next cell in TableView is highlighted by VoiceOver instead of 4th cell in ‘Popular Activities’ Collection View)

How can we circumvent this iOS Accessibility bug/limitation?

Fortunately, iOS provides an overridable method that allow the developer to specify the exact number of accessible items inside a UI element.

From Apple Developer API Documentation:

In this case accessibilityElementCount() is an overridable method supplied by NSObject. Which in turn means it can be overridden in every single kind of of iOS GUI Element. 

Hence, our solution for VoiceOver’s counting issue will be to do the following:

  1. Write a custom iOS UI Class definition that inherits UICollectionView as a base class
  2. Override the Accessibility accessibilityElementCount() method
  3. Have accessibilityElementCount() return the specific number of cells inside the UICollectionView.
  4. Replace the property usage of UICollectionView inside the UITableViewCell definition with our new custom class
This will tell VoiceOver specifically how many elements it should look for in the CollectionView, therefore fixing the issue where it only counts the first 3 in the Viewport/visible screen area.

#1 Create a new blank C# file in a Xamarin.IOS Project, subclass UICollectionView, Add the ‘Register’ attribute to expose the class to Interface Builder in XCode, then implement the following constructor

#2-3 Next we will override the accessibilityElementCount() method and have it return the number of items inside our custom Collection View. We grab our Collection View’s DataSource, ask it for how many items there are in Section 0, then return that value. If there is no DataSource set, we return 0 items. In addition, notice this code assumes our Collection View only has 1 section. We’ll cover how to count for multiple sections later.  


But wait, why is there an error overriding the method? Visual studio says it doesn’t exist! What is wrong here?

To understand what’s wrong we have take a detour to Binding Native iOS code in C#

Native iOS development in Xamarin is more often not has identically-named classes, methods, types, etc. as their original counterparts in Swift or Objective-C. Xamarin.iOS serves as a C# wrapper over the entire iOS SDK. This is done in Xamarin by writing C# methods with identical names and parameters and then binding them to the original Swift or Objective-C method. 

What is method binding?

In the case of Xamarin iOS development, a method binding is a C# method that when called invokes a native method in Objective-C.

Example of method binding directly from the Xamarin.iOS SDK 

Every single iOS SDK Class in C# is really a collection of method bindings that invokes the original SDK methods written by Apple in Objective-C. Above the C# method signature is an attribute. Attributes are C# metadata that the compiler’s preprocessor applies to the code at compile-time. Note that the custom attributes put above the property ‘UICollectionViewAppearance’ tell the C# compiler to connect this property’s value from the value of the same name in UIKit’s Objective-C library.

Although C#, Objective-C, and Swift are all object-oriented, imperative programming languages there are design differences that don’t translate perfectly across the 3 languages. Because of this, the developers of Xamarin.iOS framework have not mapped/written a binding for every single available class or method available in the native iOS SDK or named every single one identically.

Knowing this, How do we fix that compiler error complaining about the method acessibilityElementCount() and still use it?

By writing our own method binding!

We know that in native Objective-C, because accessibilityElmentCount() is a member method of NSObject, and UICollectionView inherits from UIView, which in turn inherits from NSObject we know that this method does exist. But the developers of the Xamarin.iOS SDK have not written a direct binding for it. So we just need to write our own binding for it so we can access it in the C# world.

In C# we write method bindings by applying C# attributes to the C# method.

Microsoft has an entire guide about all the C# attributes used for writing Objective-C bindings located at:

https://docs.microsoft.com/en-us/xamarin/cross-platform/macios/binding/binding-types-reference

In this case we’re going to be using the Export attribute which tells the compiler ‘Send this C# method down to the underlying Objective-C class’. In this particular scenario, doing this will replace/override the existing Objective-C method we can’t see with this new method we’ve written in C#. Notice the case difference in the string passed to the Export attribute. As we need to pass the string exactly as the real method in Objective-C. In addition, our return type will be nint instead of int as this tells the compiler this integer is from the Objective-C world.

As promised earlier, we can modify the above code to account for multiple sections by iterating over the sections and counting up each element. (If applicable)

Lastly, before finishing our custom Collection View class. We should also disable the horizontal scroll bar when VoiceOver is enabled, as we don’t want VoiceOver detecting the scroll bar as being a selectable item. If we don’t we can end up like the screenshot below.

Hence we can modify our previously-empty custom class’ constructor to the following to prevent this.

#4 Now replace usages of Collection Views inside a Table View Cell with our new custom class

We’ll need to open up the Table View’s XIB definition if using one and delete the IBOutlet Connection and re-create it using the new custom class

After doing this, saving, and quitting XCode our file’s Xamarin Designer.cs will automatically update and it should look like the following

Saving and re-building our app with VoiceOver enabled we can now see that VoiceOver allows us to horizontally scroll to the end of the carousel!

Additional Resources: