sarasvati
sarasvati

Reputation: 792

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:

Upvotes: 1

Views: 1983

Answers (2)

Peter Hornsby
Peter Hornsby

Reputation: 4266

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.

Upvotes: 1

Yuchen
Yuchen

Reputation: 33036

Actually, your view controller did not become the first responder. According to the documentation of canBecomeFirstResponder, as long as you override this function and return false, in which case you did, it won't become the first responder.

If you try put the following somewhere in your code, for example, in your motionBegan function, you will see that it shows that it is indeed not the first responder when it receive the motion event.

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

The reason why this function still gets called is because there is not a first responder which can handle this even anywhere else in your code. According to Apple's documentation about 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.

If you want to know more about how the responder chain, I will recommend you to do some more reading here which explain how the chain is set up and how the events are passed along.

enter image description here

Upvotes: 0

Related Questions