为什么这个 UIViewController 变成了 First Responder?

Why did this UIViewController become the First Responder?

我根据 XCode 的“Single View Application”模板创建了一个新的 iOS 应用程序,并且只修改了 UIViewController:

class ViewController: UIViewController {
    override func canBecomeFirstResponder() -> Bool {
        return false
    }

    override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
        print("motionBegan")
    }
}

问题是,当我摇动设备时,函数 motionBegan 被调用,尽管 canBecomeFirstResponder returns false.

据我所知,motionBegan 应该在响应链中的 UIResponder 上调用(在这种情况下,从 First Responder 开始,因为我们讨论的是非触摸事件)。

我的问题是:

实际上,您的视图控制器并没有成为第一响应者。根据canBecomeFirstResponder的文档,只要你覆盖了这个函数和returnfalse,在你覆盖的情况下,它不会成为第一响应者。

如果您尝试将以下代码放在您的代码中的某处,例如,在您的 motionBegan 函数中,您会看到它表明它在接收到动作事件时确实不是第一响应者。

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    print("touchesBegan")          // OUTPUT: touchesBegan
    print(isFirstResponder())      // OUTPUT: false
}

这个函数仍然被调用的原因是因为没有一个第一响应者可以在你的代码的任何其他地方处理这个。根据 Apple 关于 Motion Events 的文档:

Motion events use the responder chain to find an object that can handle the event. When the user starts shaking the device, iOS sends the first motion event to the first responder. If the first responder doesn’t handle the event, it progresses up the responder chain.

如果您想了解有关响应链的更多信息,我建议您多读一些书 here,其中解释了链是如何设置的以及事件是如何传递的。

设置初始First Responder的对象是什么?

设置初始第一响应者的对象是UIWindow。

From the Event Handling Guide for iOS:

Motion and remote control events. With these events, the window object sends the shaking-motion or remote control event to the first responder for handling. The first responder is described in The Responder Chain Is Made Up of Responder Objects.

应用程序启动后"Single View Application"中的初始First Responder是什么? (是UIViewController视图中最后添加的子视图吗?)

必须发生一个事件才能设置初始的第一响应者,在本例中是视图控制器,因为它覆盖了动作开始方法。

事件通过响应链机制处理,并一直传递,直到找到可以处理它的对象。触摸事件和运动事件的路径略有不同,请参阅 Event Handling Guide for iOS 中的 Event Delivery: The Responder Chain 了解详细信息。

您似乎在关注 Detecting Shake-Motion Events with UIEvent 中的信息。重写这两个方法的状态。

    override func canBecomeFirstResponder() -> Bool {
    return false
}

override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
    print("motionBegan")
}

我做了一个与您的应用程序相同的快速测试应用程序,并注意到设置 'canBecomeFirstResponder() ' 与视图控制器是否处理运动事件无关