What makes an element accessible? And why are tap gestures less accessible than UIButtons? How do I fix it? - ios

Brent Simmons wrote in a blog post that tap gesture recognizers, presumably on a UIView, are less accessible than UIButtons. I'm trying to learn my way around making my app accessible, and I was curious if anyone could clarify what makes that less accessible than a UIButton, and what makes an element "accessible" to begin with?
For more customizability I was planning to build a button comprised of a UIView and tap gesture recognizers with some subviews, but now I'm not so sure. Is it possible to make a UIView as accessible as a UIButton?

Accessible in this context most likely refers to UI elements that can be used using Apple's accessibility features, such as VoiceOver (see example below).
For example, a visually impaired person will not be able to see your view or subviews, or buttons for that matter; but the accessibility software "VoiceOver" built into every iOS device will read to her/him the kind of object and its title, something like "Button: Continue" (if the button title is "Continue").
You can see that most likely the tap gesture recognizer will not be read by VoiceOver and thus be less "accessible".

Related

Tappable Dynamic Links With UILabel

I am looking for a way, that does not involve a UITextView or 3rd party framework, to make multiple tappable links, on a label. A side note, with the string, for the label is, it’s fetched from a server, so we don’t know the links ahead of time.
As an example, lets say there’s a post, and it has two links, each to a random site dynamically fetched from the server. After detecting that they are links, which I can do, how would I make those tappable?
I have scoured StackOverflow, there have been no duplicates or this question, that I can find. If you find something exactly like this question, feel free to mark it as a duplicate. All I’ve seen were people recommending to use a UITextView, a 3rd party framework, or to use attributed strings, but the links are known at that point, they aren’t dynamically fetched.
As #DonMag pointed out, in the comments of the question - I could in fact use a UITextView, and disabling scrolling, which fixed the original reason I was having, that made me turn to do it with a UILabel.
If you would like to do it with a UILabel class then:
set 'isUserInteractionEnabled' property to true
create a Tap Gesture Recogniser and assign it to the gesture recognisers collection outlet of the label
write 'IBAction func didTapLink(_ sender: Any)' which opens a selected link and connect is with the tap gesture recogniser 'sent action selector' outlet
set 'text' property with a link
if you must to display a link as in a browser then:
set 'attributedText' property with a link
add 'NSAttributedString.Key.link' attribute to the link range

ios voiceover slider double tap and hold, but for custom view

I've created a custom view that acts like a UISlider - there is a "track", and a handle to "grab" to change the value. For particular reasons, I can't just make it a subclass of UISlider. I'm trying to make this slider as accessible as possible with VoiceOver. I have accessibilityIncrease and accessibilityDecrease on my custom view that handle single finger drag up and single finger drag down. This changes the value of the slider by 10% at a time.
However, I'd like to allow more fine grained control, just like a non-VoiceOver slider. By default , UISlider has double tap and hold, and you can drag up/down to "pan" the slider. I'd like to add exactly that to my custom view, but I can't find the correct incantation to handle the double tap and hold gesture.
Is there something I can do to mimic the double tap and hold gesture from UISlider on my custom view?
Thanks very much!!!
If you want to implement this kind of new gesture for VoiceOver users, just forget it.
The recommended gesture for this kind of UI control is definitely the implementation of adjustable value as you already did apparently.
I don't think it's a good idea to try and implement new VoiceOver gestures in an application because its users have their habits and they may be totally lost with your customed control if they cannot handle it unless you add an hint to explain but that's definitely not what I recommend anyway.
Otherwise, you could take a look at the pass through concept introduced in the What's New in Accessibility WWDC 2017 video that deals with the same idea but for a panning gesture...

3dtouch to present(peek without pop) UIView like contacts app

I'm trying to implement 3D Touch feature that presents a summary of information (like Peek). But I don't want that it pops. I just want to preview the information like contacts app does with contatcs:
It only presents an UIView and doesn't deal with two levels of force (peek and pop).
How can I do something like this?
Ps.: I don't want to deal with long press gesture.
Introduction
Hello
I know this is a bit to late, probably, but in case someone else stumbles upon it: I certainly believe it is possible and I don't think its a "native behavior for contacts". Although it would not be as simple as the UIKit api for peek pop views. You would need to:
Steps
subclass UIGestureRecognizer (perhaps it may work with the UITapGestureRecognizer also), and register UITouches and use their force property.
Setup a UIViewController with transparent but blurred background around the edges (together with a modalPresentationStyle .overCurrentContext if i recall correctly), with your desired content in the middle (much like the peek view). Then add a UIPanGestureRecognizer to the center view for dismissal/sliding up the buttons.
And then create a custom animation transition for that UIViewController to be triggered once the force property of the registered UITouches from the subclassed UIGestureRecognizer is high enough. And then reversed once the force property gets low enough.
Concluding notes
I believe this is a bit of a tedious task and there might be a simpler way. For example, a simpler way using a 3rd party library for long pressure gestures (that registers size of the touch), but it would not give the same feel.

Prevent UISegmentedControl segment selection on focus on tvOS

I'm working on a simple UI on a tvOS app and I'm facing a strange problem.
When a UISegmentedControl get focused you can move your focus around and it automatically changes the selected segment. But what I'm looking for is a way to limit the segment selection only when the user taps the segment, not when he focused it.
Any idea?
Thanks in advance.
You need to have your own internal variable for the selected segment and only change its value when the select button is pressed (which you can get using a gesture recognizer). When the segment loses focus (detectable in didUpdateFocus function) you assign the value of your internal variable to the selected index of the segment control.
You need to subclass UISegmentedControl then override didUpdateFocusInContext. In the "Custom Class" field in IB use the name of your custom class.
You can subclass UISegmentedControl and disable the behavior by defining:
#objc func _selectFocusedSegment(){
print ("select focused segment")
}
Beware that this solution is a hack. As far as I know there is no good, clean way to accomplish what you want short of steering clear of UISegmentedControl.
Also know that when a UISegmentedControl 'changes focus' between segments, it does not actually change focus. So hooking into focus updates like Nostradamus is suggesting will not work. To the focus engine UISegmentedControl behaves like a single large focusable element, not like a group of focusable segments. You can see this for yourself by debug inspecting a UIFocusUpdateContext on focusing towards or away from a UISegmentedControl.
I stumbled onto _selectFocusedSegment by defining a UISegmentedControl subclass and debug logging the various NSObject.perform methods, among others. My intent was to reverse engineer how UISegmentedControl retains a sticky last focused item, which is quite difficult to do on Apple TV. I was not able to find out exactly how UISegmentedControl manages focus, but I was able to find the answer to your question along the way.

Gestures that steal touches like iOS multitasking swipe

I know what I want to do, but I'm stumped as to how to do it: I want to implement something like the iOS multitasking gestures. That is, I want to "steal" touches from any view inside my view hierarchy if the number of touches is greater than, say, two. Of course, the gestures are not meant to control multitasking, it's just the transparent touch-stealing I'm after.
Since this is a fairly complex app (which makes extensive use of viewController containment), I want this to be transparent to the views that it happens to (i. e. I want to be able to display arbitrary views and hierarchies, including UIScrollViews, MKMapViews, UIWebViews etc. without having to change their implementation to play nice with my gestures).
Just adding a gestureRecognizer to the common superview doesn't work, as subviews that are interaction enabled eat all the touches that fall on them.
Adding a visually transparent UI-enabled view as a sibling (but in front) of the main view hierarchy also doesn't work, since now this view eats all the touches. I've experimented with reimplementing touchesBegan: etc. in the touchView, but forwarding the touches to nextResponder doesn't work, because that'll be the common superview, in effect funnelling the touches right around the views that are supposed to be receiving them when the touchView gives them up.
I am sure I'm not the only one looking for a solution for this, and I'm sure there are smarter people than me that have this already figured out. I even suspect it might not actually be very hard, and just maybe my brain won't see the forest for the trees today. I'm thankful for any helpful answers anyway :)
I would suggest you to try using method swizzling, reimplementing the touchesbegan on UIView. I think that the best way is to store in a static shared variable the number of touches (so that each view can increment/decrement this value). It's just a very simple idea, take it with a grain of salt.
Hope this helps.
Ciao! :)
A possible, but potentially dangerous (if you aren't careful) approach is to subclass your application UIWindow and redefine the sendEvent: method.
As this method is called for each touch event received by the app, you can inspect it and then decide to call [super sendEvent:] (if the touch is not filtered), or don't call it (if the touch is filtered) or just defer its call if you are still recognizing the touch.
Another possibility is to play with the hitTest:withEvent: method but this would require your stealing view to be placed properly in the subview, and I think it doesn't fit well when you have many view controllers. I believe the previous solution is more general purpose.
Actually, adding a gesture recognizer on the common superview is the right way to do this. But it sound like you may need to set either delaysTouchesBegan or cancelsTouchesInView (or both) to ensure that the gesture recognizer handles everything before letting it through to the child views.

Resources