How to debug who is eating my touches in UIKit?

前端 未结 7 1488
孤街浪徒
孤街浪徒 2021-01-30 17:16

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, whe

相关标签:
7条回答
  • 2021-01-30 17:27

    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.

    0 讨论(0)
  • 2021-01-30 17:38

    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)
        }
    }
    
    0 讨论(0)
  • 2021-01-30 17:43

    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.

    0 讨论(0)
  • 2021-01-30 17:45

    A basic way to see what is consuming the touch events is to simply add a debug view and override the hitTest(:event:) method. Attach this class to any to the view in the storyboard.

    class DebugView: UIView {
        override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
            let view = super.hitTest(point, with: event)
            print("View")
            print(view)
            return view
        }
    }
    

    Attaching this to the root view of the controller will give you access to the view that is consuming the touch event. Note that the hitTest method will be called twice with every touch event.

    0 讨论(0)
  • 2021-01-30 17:46

    To see more detail about which kind of gesture recognizers is source of issue from @Peter's answer, I print out the array of gestureRecognizer:

    class TestWindow:UIWindow {
        override func sendEvent(_ event: UIEvent) {
    
            event.allTouches?.forEach() { touch in
                print("touch")
                print(touch.gestureRecognizers ?? "")
                /// [<UILongPressGestureRecognizer: 0x7fb205964ab0; state = Possible; view = <UIView 0x7fb205964940>; target= <(action=_handleMenuGesture: ....
            }
    
            super.sendEvent(event)
        }
    }
    
    
    0 讨论(0)
  • 2021-01-30 17:48

    You can debug that by using a symbolic breakpoint:

    -[UIWindow sendEvent:] & po $arg3

    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}
    )}
    
    0 讨论(0)
提交回复
热议问题