Simulate arrow keys with 'j' and 'k' in an NSTableView? - cocoa

I have an NSTableView with a number of rows. The allowsEmptySelection property is NO, so there is always a row selected. I can move up and down the tableview with the arrow keys as expected.
I'd like to also be able to move up and down with the 'j' and 'k' keys. I've looked at the Cocoa Event-Handling Guide, but can't figure out how to make these keys simulate the up and down arrow keys.
For what it's worth, here's what I'm currently doing. I'm not really 'simulating the arrow keys'. Rather, I'm just doing the behavior I want when 'j' and 'k' are pressed. It's fine, but I'm wondering if there is a better way...
- (void)keyDown:(NSEvent *)theEvent {
NSInteger row = [self.tableView selectedRow];
NSInteger numRows = [self.tableView numberOfRows];
switch ([theEvent keyCode]) {
case 38: // 'j'
if (row < numRows - 1) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row+1] byExtendingSelection:NO];
}
break;
case 40: // 'k'
if (row > 0) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row-1] byExtendingSelection:NO];
}
break;
default:
[super keyDown:theEvent];
}
}

Create a custom subclass of NSTableView to catch the events. Your subclass should have this method in it:
- (void)keyUp:(NSEvent *)event {
if([event keyCode] == 26) {//J
if([event modifierFlags] & NSShiftKeyMask) [self moveDownAndModifySelection:nil];
else [self moveDown:nil];
} else if([event keyCode] == 28) {//K
if([event modifierFlags] & NSShiftKeyMask) [self moveUpAndModifySelection:nil];
else [self moveUp:nil];
} else [super keyUp:event];
}
This will catch any J or K keys and tell the table view to move up or down, respectively. Also, if the shift key is pressed, this will add to the selection upwards or downwards.
If you choose to use this code, make sure you filter out if any other modifiers are pressed and pass them to super also. I did not do this to make it more readable.
Edit: How to create a false event
unichar theChar = NSUpArrowFunctionKey; //NSDownArrowFunctionKey
NSString *string = [NSString stringWithCharacters:&theChar length:1];
NSEvent *newEvent =[NSEvent keyEventWithType:NSKeyUp location:[event locationInWindow] modifierFlags:[event modifierFlags] timestamp:[event timestamp] windowNumber:[event windowNumber] context:nil/*get graphics context if you want*/ characters:string charactersIgnoringModifiers:string isARepeat:[event isARepeat] keyCode:theChar];
[super keyUp:newEvent];

Related

Detect If Return/Enter Key Is Pressed on Mac OS X

I want to capture the press of the Enter key. For the keyDown: and keyUp: methods, when I output the NSEvent I get 36 as the value, but when I try to do something with that value it's unresponsive. Does anyone know what the value is and how to accomplish this with the keyDown/keyUp methods? I was looking for some sort of mapping of the keys but I couldn't find one.
- (void) keyDown:(NSEvent *)theEvent
{
NSString *eventChars = [theEvent charactersIgnoringModifiers];
unichar keyChar = [eventChars characterAtIndex:0];
if (( keyChar == NSEnterCharacter ) ||
( keyChar == NSCarriageReturnCharacter ))
{
//do stuff
}
}

"invalid operands of types 'objc_object*' and 'float'" error?

When I try testing my game in xcode simulator I get these errors below. Anyone have an idea what's wrong. Btw the behaivor is for unlocking new levels if actor collides with an object:
Error in line 49: invalid operands of types 'objc_object*' and 'float'
to binary
Error in line 58: invalid operands of types 'objc_object*' and 'float'
to binary
Error in line 73 :iso c++ forbids comparison between pointer and
integer
Here is the IOS code:
Line 49:
if(([mActor touchedActor] && ([self getGameAttribute:#"Highest Level Completed"] >= [self asNumber:[NSNumber numberWithFloat:_levelnumber]])))
Line 58:
if(([self getGameAttribute:#"Highest Level Completed"] >= [self asNumber:[NSNumber numberWithFloat:_levelnumber]]))
Line 73:
if(([mActor touchedActor] && (([self getGameAttribute:#"Highest Level Completed"] <= 5) && [[Game game] purchasedProduct:#"MyID"])))
All of the code for the behavior:
#import <Box2D/Box2D.h>
#import <AudioToolbox/AudioServices.h>
#import <Foundation/Foundation.h>
#import "ActorScript.h"
#import "Script.h"
#import "Actor.h"
#import "ActorType.h"
#import "Assets.h"
#import "Behavior.h"
#import "Collision.h"
#import "CollisionPoint.h"
#import "Game.h"
#import "GameModel.h"
#import "GroupDef.h"
#import "FadeInTransition.h"
#import "FadeOutTransition.h"
#import "Region.h"
#import "Runnable.h"
#import "Scene.h"
#import "SHThumbstick.h"
#import "Sparrow.h"
#import "Transition.h"
#interface Design_190_190_Clicktoplaylevel : ActorScript
{
#public
NSString* tempHolder;
Scene* _Scenetoplay;
NSString* _Levellabel;
float _levelnumber;
}
#end
#implementation Design_190_190_Clicktoplaylevel
-(void)load
{
[self addWhenUpdatedListener:nil func:^(NSMutableArray* list, Script* theScript){
Design_190_190_Clicktoplaylevel* self = (Design_190_190_Clicktoplaylevel*) theScript;
if(([mActor touchedActor] && ([self getGameAttribute:[self asNumber:[NSNumber numberWithFloat:[[self getGameAttribute:#"Highest Level Completed"] floatValue]]]] >= [self asNumber:[NSNumber numberWithFloat:_levelnumber]])))
{
[self setGameAttribute:[[self getGameAttribute:#"Current Level"] floatValue] value:[NSNumber numberWithFloat:[self asNumber:[NSNumber numberWithFloat:_levelnumber]]]];
[self switchScene:_Scenetoplay.ID leave:[self createFadeOut:((1000*1)) color:0] enter:[self createFadeIn:((1000*1)) color:0]];
}
}];
[self addWhenDrawingListener:nil func:^(SPRenderSupport* g, int x, int y, BOOL screen, NSMutableArray* list, Script* theScript){
Design_190_190_Clicktoplaylevel* self = (Design_190_190_Clicktoplaylevel*) theScript;
if(([self getGameAttribute:[self asNumber:[NSNumber numberWithFloat:[[self getGameAttribute:#"Highest Level Completed"] floatValue]]]] >= [self asNumber:[NSNumber numberWithFloat:_levelnumber]]))
{
[mActor setAnimation:#"Animation 1"];
[[Game game] setFont:[ self getFont:52] size:1];
[[Game game] drawString:_Levellabel x:(([mActor getWidth] - [[Game game] getWidthForString:[ self getFont:52] string:_Levellabel]) / 2) y:30];
}
else
{
[mActor setAnimation:#"Animation 0"];
}
}];
[self addWhenUpdatedListener:nil func:^(NSMutableArray* list, Script* theScript){
Design_190_190_Clicktoplaylevel* self = (Design_190_190_Clicktoplaylevel*) theScript;
if(([mActor touchedActor] && (([self getGameAttribute:[self asNumber:[NSNumber numberWithFloat:[[self getGameAttribute:#"Highest Level Completed"] floatValue]]]] <= [self asNumber:[NSNumber numberWithFloat:5]]) && [[Game game] purchasedProduct:#"MyID"])))
{
[self setGameAttribute:[[self getGameAttribute:#"Current Level"] floatValue] value:[NSNumber numberWithFloat:[self asNumber:[NSNumber numberWithFloat:_levelnumber]]]];
[self switchScene:_Scenetoplay.ID leave:[self createFadeOut:((1000*1)) color:0] enter:[self createFadeIn:((1000*1)) color:0]];
}
else if((([mActor touchedActor] && [self sameAs:[self getGameAttribute:[self asNumber:[NSNumber numberWithFloat:[[self getGameAttribute:#"Highest Level Completed"] floatValue]]]] two:[NSNumber numberWithFloat:[self asNumber:[NSNumber numberWithFloat:5]]]]) && !([[Game game] purchasedProduct:#"MyID"])))
{
return;
}
}];
}
-(void)forwardMessage:(NSString*)msg
{
It looks like in all three cases the issue is the same - you are trying to compare NSNumber objects. The comparison operators (<= & >= in your case) are not overloaded to accept NSNumber.
NSNumber itself has three methods: isEqualToNumber:, isEqual:, and compare:; which will compare two NSNumber instances.
You can also compare the value of the NSNumber using floatValue, inValue, et al. So for example your last comparison could be:
[self getGameAttribute:#"Highest Level Completed"].intValue <= 5
or using method, instead of property, syntax:
[[self getGameAttribute:#"Highest Level Completed"] intValue] <= 5
BTW You appear to have your game level stored as a floating point number. I obviously don't know your application, but storing it as an integer would seem both more logical and avoid an issues with floating point rounding (depending on the operations you perform on them).

How to put a character limit on a UITextField

I would like to put a character limit on a UITextField but don't know how. I want it to have a maximum of 16 characters in it. How do I do this.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 12) ? NO : YES;
}

Problem with NSNumber and plotting a graph with Core-Plot

I try to plot a Bar Chart with Core-Plot with an Array (content are NSIntegers) given one view before.
After transfering the Array in an NSInteger, i must convert it into a NSDecimalNumber, and in this process, my NSInteger (for example 45) becomes "60900224"...
Here's the code extract:
-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{ NSInteger *values = [Werte objectAtIndex:index];
NSDecimalNumber *num = nil;
if ( [plot isKindOfClass:[CPBarPlot class]] ) {
switch ( fieldEnum ) {
case CPBarPlotFieldBarLocation:
num = (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:index];
break;
case CPBarPlotFieldBarLength:
//num = (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:(index+1)*(index+1)];
num = [NSNumber numberWithInt:values];
if ( [plot.identifier isEqual:#"Bar Plot 2"] )
num = [num decimalNumberBySubtracting:[NSDecimalNumber decimalNumberWithString:#"10"]];
break;
}
}
return num;
}
Thanks for help!!
NSInteger is not an object type and can't be stored in an NSArray (which your Werte appears to be). You seem to be implicitly converting from a pointer to an integer.
Instead, you should always put NSNumber objects into the array, and then get NSInteger values out of those via integerValue:
NSInteger value = [[Werte objectAtIndex:index] integerValue];

Cocoa - Trim all leading whitespace from NSString

(have searched, but not been able to find a simple solution to this one either here, or in Cocoa docs)
Q. How can I trim all leading whitespace only from an NSString? (i.e. leaving any other whitespace intact.)
Unfortunately, for my purposes, NSString's stringByTrimmingCharactersInSet method works on both leading and trailing.
Mac OS X 10.4 compatibility needed, manual GC.
This creates an NSString category to do what you need. With this, you can call NSString *newString = [mystring stringByTrimmingLeadingWhitespace]; to get a copy minus leading whitespace. (Code is untested, may require some minor debugging.)
#interface NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace;
#end
#implementation NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace {
NSInteger i = 0;
while ((i < [self length])
&& [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[self characterAtIndex:i]]) {
i++;
}
return [self substringFromIndex:i];
}
#end
This is another solution using Regular Expressions (requires iOS 3.2):
NSRange range = [string rangeOfString:#"^\\s*" options:NSRegularExpressionSearch];
NSString *result = [string stringByReplacingCharactersInRange:range withString:#""];
And if you want to trim the trailing whitespaces only you can use #"\\s*$" instead.
This code is taking blanks.
NSString *trimmedText = [strResult stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",trimmedText);
Here is a very efficient (uses CoreFoundation) way of doing it (Taken from kissxml):
- (NSString *)trimWhitespace {
NSMutableString *mStr = [self mutableCopy];
CFStringTrimWhitespace((CFMutableStringRef)mStr);
NSString *result = [mStr copy];
[mStr release];
return [result autorelease];
}
NSString *myText = #" foo ";
NSString *trimmedText = [myText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSLog(#"old = [%#], trimmed = [%#]", myText, trimmedText);
Here's what I would do, and it doesn't involve categories!
NSString* outputString = inputString;
NSRange range = [inputString rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]
options:0];
if (range.location == 0)
outputString = [inputString substringFromIndex: range.location + range.length];
This is much less code.
I didn't really have much time to test this, and I'm not sure if 10.4 contains the UTF8String method for NSString, but here's how I'd do it:
NSString+Trimming.h
#import <Foundation/Foundation.h>
#interface NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront;
#end
NSString+Trimming.m
#import "NSString+Trimming.h"
#implementation NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront
{
const char *cStringValue = [self UTF8String];
int i;
for (i = 0; cStringValue[i] != '\0' && isspace(cStringValue[i]); i++);
return [self substringFromIndex:i];
}
#end
It may not be the most efficient way of doing this but it should work.
str = [str stringByReplacingOccurrencesOfString:#" " withString:#""];

Resources