Biologically Inspired Computation Series

description

Biologically Inspired Computation Series

Genetic Algorithms

description

Genetic Algorithms

Hopfield Network Simulation

description

Hopfield Network

Pattern Formation Simulation

description

AICA

Rising Sun Brushes

description

Rising Sun Brushes

Shit Boots

description

Shit Boots
Looking Forward to 2007Viewer 2.1 Released

My latest adventure left me using a NSStepper to increment and decrement a NSTextField, just making the numbers go up or down by one. I set the minValue to 1 and bound the maxValue to an array controller I have, with the appropriate data. All of this works fine, including the -valueWraps behavior to always keep the value in the correct range. However, I would like to have keyboard events do the same thing as the mouse events, so I rigged up a simple custom NSTextField that intercepts keyUp: events and conforms to the -valueWraps behavior set in the NSStepper itself.

Read on to see some snippets of code with an explanation and download a sample project.

-(void)keyUp:(NSEvent *)theEvent {if ([theEvent modifierFlags] & NSNumericPadKeyMask) {
NSString *theArrow = [theEvent charactersIgnoringModifiers];

In this first bit, we are filtering out only keys pressed that are part of the numeric keypad.
Arrow keys fall into this category. The string is a string of keys pressed.

if ( [theArrow length] == 0 ) {
return; // reject dead keys
}if ( [theArrow length] == 1 ) {
unichar keyChar = [theArrow characterAtIndex:0];

Now we have a char that represents the key pressed, an arrow key still

if ( keyChar == NSUpArrowFunctionKey ) {// Setup the loop, wrapping action between bounds
if ( [[self stringValue] doubleValue] < [stepper maxValue] ) {
NSNumber *value = [NSNumber numberWithInt:[[self stringValue] intValue] + 1];
[self setStringValue:[value stringValue]];
} else if ( [[self stringValue] doubleValue] >= [stepper maxValue] ) {
[self setStringValue:@">
}
}

The above deals with the user hitting the up arrow key and the below deals with user hitting the down arrow key

else if ( keyChar == NSDownArrowFunctionKey ) {// Setup loop, wrapping action between bounds
if ( [[self stringValue] doubleValue] > 1 ) {
NSNumber *value = [NSNumber numberWithInt:[[self stringValue] intValue] - 1];
[self setStringValue:[value stringValue]];
} else if ( [[self stringValue] doubleValue] <= 1 )
[self setStringValue:[NSString stringWithFormat:@”>
}

Essentially, I set a NSTextField’s custom class to my StepperTextField. Then I set the outlet from the text field to a NSStepper I already have configured in the interface, correct with maxValue and minValue set for the NSStepper in IB. Of note, I use the keyUp: method because the keyDown: method is being captured by the field editor inside the NSTextField, so I never catch those events. This seemed like the simplest approach to me. Now when the user hits a down arrow, the number will decrement by one. An up arrow increments it by one. If the value will be over the maxValue, it gets set to 1. If it will be less than 1, it gets set to the maxValue.

Download a sample project with plenty of comments here.

6 Responses to “Key Events and the Responder Chain: Sample Code using arrow keys in custom NSTextField”

  1. Avatar r. Says:

    Thanks for the sample code..

  2. Avatar r. Says:

    Actually your code as provided does not work on a laptop, which has no numeric keypad. Since the codes you’re matching on are NS*FunctionKey types, I replaced the mask with NSFunctionKeyMask and that seems to work fine.

  3. Avatar r. Says:

    Here’s a way cleaner version that works on keyDown, supports autorepeat, doesn’t affecting the insertion point or selection, _and it does away with all your string conversion funkiness.

    -(void)awakeFromNib {
    [self setIntValue:1];

    [self setDelegate:self];
    }

    -(BOOL)
    control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command {
    BOOL result = NO;

    if (command == @selector(moveUp:)) { // up arrow pressed
    double increment = [self doubleValue] >= [stepper maxValue] ? [stepper minValue] : [self doubleValue] + [stepper increment];
    [self setIntValue:increment];

    result = YES;

    } else if (command == @selector(moveDown:)) { // down arrow pressed
    double decrement = [self doubleValue]

  4. Avatar r. Says:

    Whoops, the html form gremlins ate the rest of the code. I’m sure you can figure out the rest of the down arrow case, and when that’s done just return result. For bonus points check to make sure stepper is valid before using it.

  5. Avatar Matt Says:

    Awesomeness, thanks for the code suggestions. I’ll be playing more with that if I ever find time to play in Cocoa instead of the devilish C++ that I’m using for all my classes…

  6. Avatar Matt Says:

    hey, thanks a bunch, I have been creating (TRYING) a program, unfortunately, I am a noob so it takes me a while to make it but your tut was very helpful; about NSSteppers. If you want my program when I am done, email me at mamiller93@gmail.com :)

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comment Preview: