I am working on a view in which certain elements will be removed and re-added as accessible items depending on the state of the view. I have been able to successfully achieve the functionality I desire by setting AccessibilityElementsHidden to toggle the state.
However, I am finding that there is a brief pause (~1-2 seconds) between this field being set before the Accessibility Layout is updated, which can allow the user to highlight a deactivated accessibility element if they are moving at a reasonable pace. If they are focused on an item as it is being disabled it makes it difficult to re-orient oneself in the VoiceOver interface.
I have found methods to immediately update the display of the interface (by means of SetNeedsLayout() and LayoutIfNeeded() on the main thread) but unfortunately this does not trigger the Accessibility Layout update.
I have also tried using UIAccessibilityPostNotification.LayoutChanged but like the changing of the AccessibilityElementsHidden property, this also takes a moment to propagate to the view.
Can anyone provide some insight as to what I need to do to ensure the user cannot put themselves in a bad state before the Accessible Layout is applied?
Related
I have a custom control to increment and decrement values. Now that I've added support for voice over, I've stumbled upon a problem.
My customView has the accessibility trait .adjustable and I implemented the correct methods for increasing and decreasing the values.
However, the voice over user can also double tap on that view to activate it. The problem is, that this triggers a gesture which is irrelevant to voice over users.
Is there a way to prevent an adjustable accessibility view from being activated so that the element is only adjustable, not double-tappable like a button?
There are two important properties to know when a double-tap occurs:
accessibilityActivate.
accessibilityActivationPoint.
In your case, you could just return true by overriding accessibilityActivate and if it's not enough, provide as well a CGPoint coordinate that triggers nothing (depends of your custom control and its neighborhood).
Otherwise, use the accessibilityElementIsFocused instance method to know wether you can trigger actions as this complete example shows up.
I ended up using UIAccessibility.isVoiceOverRunning to stop any tasks which would be triggered by a doubletap on that specific element.
I'm trying to make my app accessible (use voice-over properly).
It works just fine in the first screen (login), but after the login no element gets the accessibility focus. It seems to be stuck.
Accessibility inspector's audit gives me this issue for all of the "should-be-accessible" elements in the screen:
This element appears to display text that should be represented using the accessibility API
When pressing the question mark I get:
Determine if any part of the content should be exposed as separate accessibility children
Does anybody have an idea? Did you get this warning?
P.S.
Apologize in advance, but I can't share my code because of security reasons.
Solved my problem.
Apparently, i've added another view after the login and then animated it off screen, but did not remove from superview.
It caused the app to lose accessibility focus (the focus was on the status bar only).
After removing the view, my app got the accessibility focus again.
My lesson from this problem - remove unnecessary views!
P.S
It's legacy code - wasn't written by me :)
You can follow three things :
You can all the view hierarchy and check whether accessibilityElements are properly set or not.
Unnecessary views isAccessibilityElement property should be set as false.
remove Unnecessary views.
I have an app where I blur the navigationbar and tabbar. Here I use FXBlurView to easily do this. I have noticed that the blur keeps being refreshed even though the view doesn't change. This causes the battery to drain quicker than I would want.
Is there a way to quickly detect if the view has changed, without having to take a snapshot (which also is +-5ms each time)?
You can use UIScrollViewDelegate methods for scroll changes and easily set dynamic property to true/false
As another option you can change updateInterval to a bigger value to provide delayed refreshes.
I implemented a custom iOS control (looking similar to a ruler), where the user can easily scroll to select one of about 300 values.
My control also has the trait UIAccessibilityTraitAdjustable and implements the methods accessibilityIncrement() and accessibilityDecrement() of the protocol UIAccessibilityAction to update its value. So far everything works as intended.
The problem is that adjusting the value via VoiceOver can be tedious, since each swipe gesture increments/decrements the value only by one. Is there a good way to offer a second mode where the updates happen, e.g. in steps of ten?
UIAccessibilityTraitAdjustable uses a fixed, developer-defined increment size. You can adjust the slider with a larger step size in -accessibilityIncrement if you think users will find it tedious to adjust the control one unit at a time. VoiceOver users desiring finer control can use the pass-through gesture (double tap and hold) to interact with the control directly, sliding to adjust it in single-percent increments. This does, however, assume users have fine motor control.
If you feel strongly that UIAccessibility should support a "fine-grain" adjustment mode, file an enhancement request with supporting use cases.
How can I set voice over focus on my element (such like UITextView) in iOS 6. Is it possible?
Very simple. Just do this:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, textView);
Note that it may not work, depending on where and when you use it. If you're trying to set the focus to a particular element from viewDidLoad, the code probably won't work correctly. This is because VoiceOver makes its own attempt to set the VoiceOver focus on an element initially; if VoiceOver does this before it processes your attempt to set focus, your code will work. But if VoiceOver gets around to setting the initial focused element after it processes your attempt, your code will appear to fail.
A somewhat more reliable way to do this in viewDidLoad is to use performSelector:withObject:withDelay:, to ensure that your call is processed after VoiceOver's initial focus setting.