Search code examples
iosswiftuiviewcontrollerfirst-respondermotionevent

Why did this UIViewController become the First Responder?


I've created a new iOS application based on the XCode's "Single View Application" template and only modified the UIViewController:

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

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

The problem is that when I shake the device, the function motionBegan gets called despite the fact that canBecomeFirstResponder returns false.

AFAIK the motionBegan should be called on an UIResponder in the responder chain (in this case starting from the First Responder since we're talking about a non-touch event).

My questions are:

  • What is the initial First Responder in the "Single View Application" after the application starts up? (Is it the lastly added subview in the UIViewController's view?)
  • What object does set the initial First Responder?

Solution

  • What object does set the initial First Responder?

    The object that set's the initial first responder is 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.

    What is the initial First Responder in the "Single View Application" after the application starts up? (Is it the lastly added subview in the UIViewController's view?)

    An event has to happen for the initial first responder to be set, which in this case is the view controller since it overrides the motion began method.

    The event is handled with the Responder Chain mechanism and is passed along until it finds an object which can handle it. Touch events and Motion events have slightly different paths see Event Delivery: The Responder Chain in the Event Handling Guide for iOS for details.

    It looks like that you are following the information in Detecting Shake-Motion Events with UIEvent. Which states to override these two methods.

        override func canBecomeFirstResponder() -> Bool {
        return false
    }
    
    override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
        print("motionBegan")
    }
    

    I did a quick test app just the same as yours and noted that setting 'canBecomeFirstResponder() ' had no bearing on whether the motion event was handled by the view controller.