Reputation: 1480
I am making a "LearnToTypeQuick" application and therefore i need to validate my keyboard presses to see if they match what i print on my view
.
I would like to use the keyDown
event in my ViewController
and therefore i need the ViewController
to become first responder.
My question is: How do i do that?
Upvotes: 2
Views: 4113
Reputation: 4100
AppKit puts the window's initialFirstResponder ahead in the responder chain, and because initialFirstResponder is not an NSResponder, but an NSView, there is no way to have NSViewController be first responder unless AppKit changes its policy in this regard which is not likely.
If your window's contentView is a vanilla NSView (which doesn't implement any of the responder callbacks) then events propagate to your view controller. Where you run into trouble is when the OS provided views do respond (and therefore, swallow) responder callbacks, such as SCNView or SKView.
What you can do (must do) in this cases to get events to your view controller is subclass the latter views, override the responder methods of interest and forward to them to the nextResponder.
I disagree with your point, Caleb:
"...make sure that any responders ahead of it don't handle the messages you're interested in."
This requires you to subclass which isn't an ideal solution. I do not think that "is the whole purpose of the responder chain," as you say.
For example, UIWindow doesn't use an initialFirstRespnoder (of a view type or any type) and so iOS doesn't suffer from this unintuitive limitation.
Furthermore, initialFirstResponder is a get/set property and yet, setting it to nil along with setting both, the main window and its NSWindowController's nextResponder to a view controller still doesn't get an obstinate view out of the responder chain (e.g., SKView or SCNView, anyway). In case it's a bug of some kind, I filed bug report #39575165.
Upvotes: 1
Reputation: 124997
I would like to use the keyDown event in my ViewController and therefore i need the ViewController to become first responder.
You probably don't actually need that. The whole point of the responder chain is that if one responder doesn't handle a given message, it passes the message on to the next responder in the chain, and so on. So, all you really need is to have your view controller in the responder chain, and make sure that any responders ahead of it don't handle the messages you're interested in.
Another strategy would, of course, be to move the key handling code into an appropriate view, and have that view send messages to a delegate (which could be the view controller) as necessary. That could save you some work in terms of updating the view that the user is typing into, etc., and it'd probably be a little more in keeping with typical application design.
A very simple implementation of this strategy would be to have the user type into a plain old NSTextView
. Many typing programs show the user some passage and have the user type the passage into some sort of text editing field. You could set your view controller as the text field's delegate. One of the delegate messages that NSTextView
provides is -textDidChange:
, so you'd implement that in your view controller (as well as adopting the NSTextViewDelegate
protocol in your view controller's declaration). If you then set the view controller as the text view's delegate, the text view will send -textDidChange:
messages every time the text in the view changes. You could then compare the text in the view to your model text and highlight any mismatches so the user can see that he/she has made a mistake.
If you really want your view controller to be the first responder, there's nothing to stop you from doing that. You can override -acceptsFirstResponder
such that it returns YES, and then send your view controller a -becomeFirstResponder
message.
You can read more about the responder chain and how key events are handled in the Cocoa Event Handling Guide.
Upvotes: 2