Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to debug who is eating my touches in UIKit?

Tags:

ios

I can't figure out why tapping on text fields and buttons in my view is not working. I've checked all the obvious things like whether userInteractionEnabled is set to YES, whether a gesture recognizer is installed, and if there is an invisible view in the foreground.

Is there a best practice in iOS for tracing a touch from when it first appears to where it gets consumed?

UPDATE:

Both answers were helpful. During the course of my investigation I learned that if a subview is outside of its parent's bounds, even if the parent is not clipping subviews, the subview will not receive events. I printed out the chain of superviews from the text field that was not getting touches, and I saw that one of those views had a height of 0. I put in some constraints to stretch it out, and my problem was solved.

like image 318
bugloaf Avatar asked Sep 29 '14 21:09

bugloaf


5 Answers

You can debug that by using a symbolic breakpoint:

-[UIWindow sendEvent:] & po $arg3

enter image description here Logs:

<UITouchesEvent: 0x6000026fa6d0> timestamp: 179462 touches: {(
<UITouch: 0x7f84d6f10380> phase: Began tap count: 1 force: 0.000 
window: <UIWindow: 0x7f84d6d0ad10; frame = (0 0; 375 812); autoresize = W+H; 
gestureRecognizers = <NSArray: 0x600001aa8870>; layer = <UIWindowLayer: 0x6000014bd7e0>> view: <UIView: 0x7f84d6d0bff0; frame = (0 0; 375 812); 
autoresize = W+H; layer = <CALayer: 0x6000014bdc60>> location in window: {165.66665649414062, 232.33332824707031} previous location in window: 
{165.66665649414062, 232.33332824707031} location in view: {165.66665649414062, 
232.33332824707031} previous location in view: {165.66665649414062,232.33332824707031}
)}
like image 107
HamzaGhazouani Avatar answered Oct 15 '22 05:10

HamzaGhazouani


You can subclass UIWindow and override -[UIWindow sendEvent]:, then when it is called, use -[UIWindow hitTest:withEvent:] to test which view will receive the event.

You can then call -[UIView recursiveDescription] to print some debug information help you understand why that view received the touch event.

Remember to call [super sendEvent:] when you are done.


For those who use Storyboard and want to know how can we change the main application window class:

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow? = TestWindow()
    // ....
}

Just provide the default value for AppDelegate's window var. When the app is launched the UIApplicationMain will instantiate a delegate and ask it for the window. If the window is nil it will create a new one automatically. But if we provide a default value here, it will be used all over the app.

like image 24
Bryan Chen Avatar answered Oct 15 '22 06:10

Bryan Chen


Not a direct answer to the question, but a very common cause of vanishing touches is for a control to be in a uiview which has a smaller size than that control, but is not clipping its bounds, so you won't see that the parent view is smaller (or even possibly zero-sized).

Parent UIView size=0x0, clipToBounds=false

   Child UIButton size=100x50

=> Child Button won't get any touch events.

like image 13
Guillaume Laurent Avatar answered Oct 15 '22 06:10

Guillaume Laurent


You can use the new Xcode6 live view debugging and by turning the view hierarchy in 3D you can see which views are above the ones you care for and inspect them.

like image 4
DeFrenZ Avatar answered Oct 15 '22 07:10

DeFrenZ


My full Swift Example following @Bryan Chen and @kelin Answer ...

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow? = TestWindow()
    // ....
}

...

class TestWindow:UIWindow {
    override func sendEvent(_ event: UIEvent) {

        print(event)

        //<UITouchesEvent: 0x6000008b1f80> timestamp: 366545 touches: {(
        //    <UITouch: 0x7fa056d37dc0> phase: Ended tap count: 1 force: 0.000 
        //window: <zollundpost.TestWindow: 0x7fa056c020a0; baseClass = UIWindow; 
        //frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x6000034f9530>; 
        //layer = <UIWindowLayer: 0x600003abd940>> view: <UITableViewCellContentView: 
        //0x7fa059a55090; frame = (0 0; 375 72); opaque = NO; gestureRecognizers = 
        //<NSArray: 0x6000034293e0>; layer = <CALayer: 0x600003ac8320>> location in 
        //window: {165, 358.33332824707031} previous location in window: 
        //{144.33332824707031, 358.66665649414062} location in view: {165, 
        //44.999992370605469} previous location in view: {144.33332824707031, 
        //45.333320617675781}
        //    )}

        super.sendEvent(event)
    }
}
like image 3
Peter Kreinz Avatar answered Oct 15 '22 06:10

Peter Kreinz