How to set VoiceOver initial focus on a UITableView - ios

In our iOS app, we have a screen with a UINavigationController, three UIBarButtonItem in the rightBarButtonItems, a UIView content view with a UITableView subview.
Every time the app gets to the foreground, VoiceOver places the initial focus on the leftmost UIBarButtonItem. We would like to have the focus on either the topmost cell in the UITableView (initial start) or the cell that the user actively selected before the app went to the background.
We have tried to call UIAccessibilityPostNotification() but this only works if we add a delay and that seems very fragile and will be confusing for the user as focus will start on the UIBarButtonItem and then jump.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.75*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.view)
}
This might work.

As far as I know uiaccessibilitypostnotification works well with the delay, even though it is fragile, you can call the method with 0.5 seconds delay in the view did appear method!!!!
There is also another way you can try. Once the view is loaded, disable the accessibility for the bar buttons, and set the accessibility only for the tableview!!! You need to change the accessibilityElements array and the first element in the array should be the table view, and set isAccessibilityElemnt to NO for the other elements to turn off the accessibility!!!!

Related

UIButton default tap animation inside UITableViewCell

I have a few UIButtons inside UITableViewCells, and all of them are missing default tap animation, however if I long press - animation works. I've found some solutions, like setting type to .system, showsTouchOnHighlight = true, but none of them have helped. What is the problem here?
It's not a "problem" - it's by design.
When you have an object such as a button in a table view or scroll view or collection view, and you "touch" it, the system needs to know whether you are tapping the button or if you want to touch-and-drag to scroll the view that contains the button.
So, the table view (really, the "scroll view part" of the table view), waits to see if you drag your finger or not before it performs any actions.
If you want the button to respond immediately to a touch, you need to set
delaysContentTouches = false
on the containing scroll view(s).
If you search for uibutton inside uitableviewcell delaysContentTouches you should find plenty of discussion on this topic, and various approaches to change the default behavior.
For this problem you can add extension to UIButton and override some methods.
You can check my answer here

Touch Down firing in a weird way

I'm adding a touch down action to a uitextfield (actually it's a subclass, but I think that might not be important). I created a simple view controller and added this textbox to it, and wired up the event to println("Hello").
When I quickly tap the item (both in simulator, and on my phone) it works perfectly and says hello!
I then created a UITableViewController subclass, and in one of the static cells I added the same textbox.
In this case, when I quickly tap the textbox nothing happens! When I actually hold down the mouse or my finger for about 1/2 a second, it works. But not if I quickly tap it.
This is different from the previous textbox, which always works perfectly no matter how fast I tap it.
Are there some problems with different events being intercepted ors something of that sort?
I even went so far as to add a tap gesture recognizer to both the table cell, and the textbox, but neither work unless I hold it down (the table cell action won't even fire unless I click off the textbox and into the cell proper, of course).
Thanks so much this is very strange.
UIButton not showing highlight on tap in iOS7
and
iOS - Delayed "Touch Down" event for UIButton in UITableViewCell
have a lot of information about this. Apparently there is a delay for uitableviewcells that can be avoided by taking some of the approaches above.
I'll post the solution that works for me once I work on it. thanks!
EDIT OP DID DELIVER!! (lol sorry)
in IOS8, the idea is that table cells no longer have the uiscrollview that would basically delay the touching, so what you can do instead is something like this in your page did load:
for subview in self.tableView.subviews as [UIView]
{
if subview is UIScrollView
{
let scroll = subview as UIScrollView
scroll.delaysContentTouches = false
break
}
}
So see how we're iterating over self.tableview's subviews, and anytime we hit a scrollview set delaysContentTouches to false. This worked for me on both the simulator and on my phone.

Showing / hiding accessibility elements in an overflow menu when opening a custom UITableViewCell

I’m implementing accessibility in a custom UITableViewCell class. I have a fairly simple overflow menu with a couple of buttons inside it, which are hidden until an ellipsis button is pushed that slides open and closes the overflow.
In my cell's initialiser I’m setting the accessibilityElementsHidden of my overflowContainer to YES. This seems to work, when scrolling through using VoiceOver, those views are skipped.
Then, when opening the cell, in the completion handler of the UIView animation, I set that same accessibilityElementsHidden of the same overflowContainer to NO. This doesn’t seem to have any effect, those elements are still skipped.
I’ve also tried posting UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil) before / after / when changing the accessibilityElementsHidden BOOL, but this also appears to have no effect on the situation.
Basically I’d like to toggle accessibilityElementsHidden on a couple of UIView instances at a specific point. Could anyone let me know what I may be doing wrong?
Here’s the code I fire when the overflow opens:
- (void)cellOverflowDidShow:(MyCell *)cell
{
self.overflowContainer.isAccessibilityElement = YES;
self.firstButton.isAccessibilityElement = YES;
self.secondButton.isAccessibilityElement = YES;
self.thirdButton.isAccessibilityElement = YES;
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.firstButton);
}
I fire the opposite when closing the cell (set all to NO and post notification again). And when initializing the cell, all I set is:
self.overflowContainer.isAccessibilityElement = NO;
Absolutely no idea why it shouldn’t be working, it appears I’m doing everything 100% correctly. If I don’t set the line in the initializer, the buttons all appear accessible (all the time). So it appears that the first call, be it YES or NO, works, but any subsequent ones are ignored.
In the visible state, you declare the overflow container to be an accessibility element. Thus, VoiceOver will allow the user to focus it rather than navigate child elements. Instead of toggling whether it's an accessibility element, keep self.overflowContainer.isAccessibilityElement set to NO and toggle the accessibility of its children, firstButton, secondButton, and thirdButton.
A shorthand for setting the accessibility of child elements is accessibilityElementsHidden. Try setting self.overflowContainer.accessibilityElementsHidden to NO when the view appears and YES when it disappears.
You may still need to trigger a layout change notification, regardless.

Unable to Interact with UISearchBar in a UITableView

The Issue
I have a UIGestureRecognizer setup that on press to any of the UITableViewCell in my UITableView, it sets the UISearchBar active. Everything works. I can press on the cell and the UISearchBar animates as it would normally. I can enter letters, tap and hold on the UITextField and zoom in to a specific cursor position. I can hit cancel and everything goes back to their proper locations.
This is where the issue comes up. I scroll down a few cell (or until the UISearchBar is hidden under the UINavigationBar) and press the cell to activate the UISearchBar. Everything seems to animate to their proper location but when I try to tap and hold to zoom in to a proper cursor on the textfield nothing happens. I am also unable to hit the cancel button. The odd part is that if I do press on any part of the UISearchBar, it becomes the first responder.
Things I have done
I played around with the contentInset of the searchResultsTableView and see if that was blocking the UISearchBar.
I played around with the frame of the searchResultsTableView and its superview to see if it is blocking the UISearchBar.
I added the methods defined in the UISearchDisplayDelegate protocol and ensure that there were no views blocking said UISearchBar.
Please help!
My goal is to be able to interact with the UISearchBar as it is intended. If you know of any other option I can please let me know!
Thank you in advance!!
Update
I found the answer at Programmatically activating UISearchBar blocks user interactions with it
There is a possibility of a timing issue when the UISearchBar is shown and when it becomes active so the solution is to delay activating it.
So when you scroll down, the tableview is actually not referencing to the original cell as a result of the way iOS tries to reuse cells to conserve memory. The way to solve this is to say if(indexPath.row==0){cell.contentview addGesture...} in the cellForIndexPath function or use the didSelectAtIndexPath function to specify what happens when you click indexPath.row 0

UISystemGateGestureRecognizer and delayed taps near bottom of screen

What are the standard UISystemGestureGateGestureRecognizers installed on the top level UIView of an iOS app for?
My app consists of two views - one fills the top half of the screen, the other is a custom keyboard and fills the bottom half. I found that taps on the space bar didn't always work and after some investigation found that the timing of tap events in the bottom 20 pixels or so was different to the rest of the view. For most of the view the period between touchesBegan/Ended was about 100ms, where as for the space bar it was 1-2ms. (My app is an emulator and this is too fast for it to detect the key press).
After some more digging I found the main UIView of the application (ie: my main view's superview) has 2 UISystemGestureGateGestureRecognizer's installed. By removing them in ViewDidAppear the bottom of the screen is no longer affected. (Presumably these are cancelling the touch press events to my keyboard hence the faster timing).
These system recognizers are present on at least iOS 5 through 7 and on both iPad and iPhone. I thought they may be related to swipe from top/bottom but this functionality still works with them removed.
So I have a fix, but I'd like to understand more about what's going on here - in particular what I might be breaking by removing these.
This delayed touches bothered me too.
Just as an addition to what's said before,
here's a simple fix:
override func viewDidAppear(_ animated: Bool) {
let window = view.window!
let gr0 = window.gestureRecognizers![0] as UIGestureRecognizer
let gr1 = window.gestureRecognizers![1] as UIGestureRecognizer
gr0.delaysTouchesBegan = false
gr1.delaysTouchesBegan = false
}
no need to remove those gesture recognizers.
just add this to the main view controller.
It appears that these recognizers are meant to prevent accidental touches near the top and bottom of the screen. They aren't configured with any targets, but can (like any UIResponder) absorb touches to prevent them from being passed up the responder chain.
Notes (tested on iOS 7.1):
Both gesture recognizers are always present in the key window.
I inspected both gestures' _targets ivar, and found they aren't configured with any targets at all. Swizzled out addTarget:action: to verify that targets weren't being added or removed on the fly.
delegate is always nil for both instances.
If you disable the gesture recognizers, they will re-enable themselves
The gesture that that doesn't delay content touches fires when you drag up from the bottom or drag down from the top. I couldn't trigger the instance that delays touches.

Resources