3

I'm learning Objective-C by building a basic calculator app for OSX. Everything works beautifully, except I need to allow pressing of certain keys on the keyboard to do the same thing as if you clicked the buttons on the interface.

Everything I read says to capture these you have to have the logic in a subclass of NSResponder. My issue comes in how I "connect" that file. Creating a new .h and .m file that subclasses NSResponder like such:

Responder.h


#import <Foundation/Foundation.h>

@interface Responder : NSResponder

   - (void)keyDown:(NSEvent *)event;

@end

Responder.m


#import "Responder.h"

@implementation Responder

- (void)keyDown:(NSEvent *)event {
    NSAlert *alert = [[NSAlert alloc] init];
    [alert addButtonWithTitle:@"OK"];
    [alert setMessageText:@"Hey"];
    [alert setInformativeText:@"You Pressed A Key!"];
    [alert setAlertStyle:NSWarningAlertStyle];
    [alert runModal];
}

@end

How does the application know to use that file? Is there somewhere in the interface for the .xib file where I drag that blue arrow and "connect" it somewhere? Am I even doing it right at all? I've been googling this for a while and everything pretty much assumes I would know how to get a NSResponder subclass into my application. I'm really missing a fundamental concept here I think. Any help is appreciated!

Jeremy Harris
  • 24,318
  • 13
  • 79
  • 133

1 Answers1

8

When people say "in a subclass of NSResponder", they don't necessarily mean that you should make a new NSResponder subclass. What they mean is this: key presses are handled by responders. Your app structure is chock full of responders! So, to intervene in the key-handling process, subclass one of those (so that you have somewhere to put the code).

The "fundamental concept" that you are missing is the responder chain:

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html

(Scroll down to the heading "Responder Chain".)

A frequent place to put this sort of code is the window controller. NSWindowController is an NSResponder subclass. It's high up in the responder chain, and you've probably already got a class for it.

Another option is to use a view. NSView is an NSResponder subclass, and your window is full of views. It wouldn't be surprising to put an otherwise inert NSView in back of everything in the window, just to function as a backstop NSResponder to catch events that come up the chain.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Super answer, this helped me to understand that better. Thanks for the effort. We are a chain :) –  Apr 07 '13 at 14:54
  • @matt, This is a good answer. I'm still missing one small piece. Right now I have an AppDelegate class which it auto-created for me. I created an AppController class which is an NSObject. Other than that, I'm guessing that .xib file is performing some "magic" and adding in the Windows/Views at compile time. I noticed that I can set a custom class for the window, is that what I should be doing? – Jeremy Harris Apr 07 '13 at 15:04
  • 1
    There is no "magic". Is the problem that you don't understand what a .xib file (nib) *is*? It's just a collection of instances, which come into existence (object instantiation) when the nib is loaded (which in this case is when the app launches). That's during runtime, not "at compile time". However, "what is a .xib/nib" would be a whole different question! – matt Apr 07 '13 at 15:30
  • 1
    I see what your confusion is, though - you don't have an NSWindowController, so that part of my answer isn't much help. But you do have a great big NSView in back of everything in your window! That's an NSResponder. You can make an NSView subclass (MyView?) and tell the nib that this big NSView should be an instance of it. Now you've got a place to inject code into the responder chain. – matt Apr 07 '13 at 15:34
  • 1
    Alternatively, your suggestion is also a good one. Subclass NSWindow and tell the nib that the NSWindow instance it contains should be an instance of your subclass. Again, now you've got a place to inject code into the responder chain (much like the diagram in the Apple doc I pointed you to). – matt Apr 07 '13 at 15:37
  • 1
    @matt, thanks for all your help, I got it working. I made a class inheriting NSWindow and overrode the keyDown method. – Jeremy Harris Apr 07 '13 at 16:03