UISystemGateGestureRecognizer and delayed taps near bottom of screen
Asked Answered
S

3

20

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.

Screw answered 5/11, 2013 at 22:4 Comment(1)
I cannot tell you what they are, but it appears there are two of them defined in UIWindow: github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/… Perhaps print their targets and we can deduce better what their function is.Haden
H
15

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.

Haeckel answered 28/11, 2014 at 12:35 Comment(3)
view.window returned a nil instance for meProportionable
This worked for me but i do get the following in my console [Warning] Trying to set delaysTouchesBegan to NO on a system gate gesture recognizer - this is unsupported and will have undesired side effects Any idea if this will prevent my app from being accepted during review??Indoor
@JessMcKenzie did you get an answer? I get the same console message.Drudge
N
7

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.
Nitroglycerin answered 11/6, 2014 at 14:18 Comment(2)
Could the delayed-trigger one be for the tap-status-bar-to-scroll-to-top functionality used in apps with scroll views? Theoretically, it would only trigger when there's a scroll view on screen. Also, since these are internal classes, they could be sending their events via untraditional means— e.g firing an NSNotification, or even calling out to a private API singleton object/method.Disserve
I get the same error also in the middle of the screen, everywhere. Don't know what to do.Appendicitis
R
2

As of iOS11, the correct way to override these gestures is to override preferredScreenEdgesDeferringSystemGestures in your view controller.

 open override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
    return .all
}
Rockribbed answered 4/5, 2023 at 22:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.