如何在 UIKit 中调试谁在吃我的触摸?

Posted

技术标签:

【中文标题】如何在 UIKit 中调试谁在吃我的触摸?【英文标题】:How to debug who is eating my touches in UIKit? 【发布时间】:2014-09-29 21:13:40 【问题描述】:

我不明白为什么在我的视图中点击文本字段和按钮不起作用。我已经检查了所有明显的事情,例如 userInteractionEnabled 是否设置为 YES、是否安装了手势识别器以及前景中是否有不可见的视图。

ios 中是否有跟踪触摸从首次出现到被使用的最佳实践?

更新:

两个答案都很有帮助。在我的调查过程中,我了解到如果子视图超出其父视图的范围,即使父视图没有剪切子视图,子视图也不会收到事件。我从没有被触摸的文本字段中打印出超级视图链,我看到其中一个视图的高度为 0。我设置了一些约束来拉伸它,我的问题就解决了。

【问题讨论】:

零尺寸的视图让我很不爽。我制作了一些中间视图来保持 z 顺序的健全,但我忘记更新中间视图的框架,所以它们的大小保持为零。一旦我在LayoutSubviews 中更新了它们,水龙头又开始流动了。 我也被这个咬了。总之,检查有这个问题的视图层次结构中的两件事(假设它没有被非交互式透明视图覆盖):userInteractionEnabled == true,如果高度和宽度> 0。 【参考方案1】:

你可以使用符号断点来调试它:

-[UIWindow sendEvent:] & po $arg3

日志:

<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
)

【讨论】:

很好的答案。如果您只想要日志而不是停止调试器的断点,我还建议检查“评估操作后自动继续”【参考方案2】:

你可以继承UIWindow并覆盖-[UIWindow sendEvent]:,然后当它被调用时,使用-[UIWindow hitTest:withEvent:]来测试哪个视图会接收到事件。

然后您可以调用-[UIView recursiveDescription] 打印一些调试信息,以帮助您了解该视图收到触摸事件的原因。

完成后记得致电[super sendEvent:]


对于那些使用 Storyboard 并想知道如何更改主应用程序窗口类的人:

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

只需为AppDelegatewindow var 提供默认值。当应用程序启动时,UIApplicationMain 将实例化一个委托并要求它提供window。如果windownil,它将自动创建一个新的。但是如果我们在这里提供一个默认值,它将在整个应用程序中使用。

【讨论】:

hitTest的第一个参数应该设置什么值? @布莱恩 一个CGPoint,可以从事件中读取。你需要先检查它是否是一个触摸事件 如果有人需要实现它,由于它使用私有 API,这里是它在 objc 中的完整代码:- (void)sendEvent:(UIEvent *)event UITouch *touch = [event.allTouches anyObject]; UIView *view = [self hitTest:[touch locationInView:touch.view] withEvent:event]; NSLog(@"Touch captured: %@", [view valueForKey:@"recursiveDescription"]); [super sendEvent:event]; 【参考方案3】:

不是对这个问题的直接回答,但触摸消失的一个非常常见的原因是控件位于尺寸小于该控件的 uiview 中,但没有剪裁其边界,因此您不会看到父视图更小(甚至可能为零)。

Parent UIView size=0x0, clipToBounds=false

   Child UIButton size=100x50

=> 子按钮不会获​​得任何触摸事件。

【讨论】:

【参考方案4】:

您可以使用新的 Xcode6 实时视图调试,并通过将视图层次结构转换为 3D,您可以查看哪些视图高于您关心的视图并检查它们。

【讨论】:

【参考方案5】:

我的完整 Swift 示例遵循 @Bryan Chen 和 @kelin 的答案 ...

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)
    

【讨论】:

【参考方案6】:

要从@Peter 的回答中了解更多关于哪种手势识别器是问题来源的详细信息,我打印出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)
    


【讨论】:

【参考方案7】:

查看触摸事件消耗的基本方法是简单地添加一个调试视图并覆盖hitTest(:event:) 方法。将此类附加到情节提要中的任何视图。

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
    

将此附加到控制器的根视图将使您能够访问正在使用触摸事件的视图。请注意,每次触摸事件都会调用 hitTest 方法两次。

【讨论】:

以上是关于如何在 UIKit 中调试谁在吃我的触摸?的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 调试内存图仅显示 UIKit

如何跨 SwiftUI 应用程序跟踪所有触摸

使用iPhone应用程序中的过渡移动要触摸的对象

如何找出谁在托管我的 Wordpress 网站? [关闭]

如何通过 SignalR 实现“谁在打字”功能?

如何使用 UIKit 在 Swift 5 中立即强制更新我的 UI?