Anyway to make a (wrapping) NSTextField write a carriage return upon pressing return key? - objective-c

I want to use a wrapping text field that can potentially contain carriage returns in my app. Is there any way to force the NSTextField object to write a carriage return into the text area instead of sending its action to its target when the Return key is pressed?

This is covered in Technical Q&A QA1454, which also enumerates reasons why one would use NSTextField instead of NSTextView in this case.
You can implement the following method in the text field delegate:
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver
// to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}

Okay, I figured out one way to do it, but this very well may not be the best (or even a good) way. I subclassed NSTextField, and overrode -textShouldEndEditing: like so:
-(BOOL)textShouldEndEditing:(NSText *)textObject {
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36) {
[self setStringValue:[[self stringValue] stringByAppendingString:#"\n"]];
return NO;
}
else {
return [super textShouldEndEditing:textObject];
}
}

I found a combination of Sean and Bevarious worked best for me. Sean's answer assumes that the new line is always wanted to be added to the end (instead of for instance where the user's cursor is placed).
-(BOOL)textShouldEndEditing:(NSText *)textObject
{
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36)
{
[textObject insertNewlineIgnoringFieldEditor:nil];
return NO;
}
else
{
return [super textShouldEndEditing:textObject];
}
}

Swift version:
override func textShouldEndEditing(textObject: NSText) -> Bool {
let event = NSApplication.sharedApplication().currentEvent
if event?.type == NSEventType.KeyDown && event?.keyCode == 36 {
self.stringValue = self.stringValue.stringByAppendingString("\n")
return false
} else {
return super.textShouldEndEditing(textObject)
}
}

Related

Two finger swipe in Yosemite 10.10

I have been using a similar method to as this:
https://github.com/oscardelben/CocoaNavigationGestures
To capture two finger swipes on the Mac, under Yosemite it is no longer working. Anyone know what has change, or what I need to change for this to work.
The accepted answer didn't work well for me - it would often not detect the swipe. Instead, I overrode wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis to return YES for the appropriate axis, then overrode scrollWheel:(NSEvent *)theEvent to detect scrolling. Works perfect every time.
The complete answer in Swift 5.3, based on #bmuller would be:
override func wantsScrollEventsForSwipeTracking(on axis: NSEvent.GestureAxis) -> Bool {
return axis == .horizontal
}
override func scrollWheel(with event: NSEvent) {
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
Multiple swipe events might be sent in a single gesture with this code. You may need to add phase(NSEventPhase) handling code to the scrollWheel(...) function e.g.
override func scrollWheel(with event: NSEvent) {
guard event.phase == .began else {
return
}
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
On your Mac, go to system preferences and you will find all the different settings of the trackpad.
From here: https://discussions.apple.com/thread/5710582
Hope I helped.
This was my solution, seems to be working for me.
#define kSwipeMinimumLength 0.2
- (void)touchesBeganWithEvent:(NSEvent *)event{
if(event.type == NSEventTypeGesture){
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self];
if(touches.count == 2){
self.twoFingersTouches = [[NSMutableDictionary alloc] init];
for (NSTouch *touch in touches) {
[self.twoFingersTouches setObject:touch forKey:touch.identity];
}
}
}
}
- (void)touchesMovedWithEvent:(NSEvent*)event {
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:self];
if(touches.count > 0){
NSMutableDictionary *beginTouches = [self.twoFingersTouches copy];
self.twoFingersTouches = nil;
NSMutableArray *magnitudes = [[NSMutableArray alloc] init];
for (NSTouch *touch in touches)
{
NSTouch *beginTouch = [beginTouches objectForKey:touch.identity];
if (!beginTouch) continue;
float magnitude = touch.normalizedPosition.x - beginTouch.normalizedPosition.x;
[magnitudes addObject:[NSNumber numberWithFloat:magnitude]];
}
float sum = 0;
for (NSNumber *magnitude in magnitudes)
sum += [magnitude floatValue];
// See if absolute sum is long enough to be considered a complete gesture
float absoluteSum = fabsf(sum);
if (absoluteSum < kSwipeMinimumLength) return;
// Handle the actual swipe
// This might need to be > (i am using flipped coordinates), you can use an else to go forward also.
if (sum > 0){
NSLog(#"go back");
}
}
}

How Can I get keyboard input in a SpriteKit Game?

I'm a beginner in SpriteKit programming, and have been trying to figure out how to handle input from the keyboard.
What I've found so far is that you should subclass NSResponder and implement it like this:
#interface AppDelegate : NSResponder <NSApplicationDelegate>
-(void)keyUp:(NSEvent *)theEvent;
-(void)keyDown:(NSEvent *)theEvent;
#end
#implementation AppDelegate
-(void)keyUp:(NSEvent *)theEvent
{
NSLog(#"Key Released");
}
-(void)keyDown:(NSEvent *)theEvent
{
NSLog(#"Key Pressed");
}
#end
Obviously, there are a few more methods/properties in the interface and implementation of AppDelegate but I didn't put them there to keep the question relevant.
Next, I would start using key codes to detect which keys are being pressed, but the keyUp and keyDown methods don't even get called. I'm not sure why.
Any Help?
Update:
Thanks for your answers! I discovered that you have to implement keyUp and keyDown directly in your scene class, because they won't get called from the AppDelegate. Thanks again for the help!
The easiest way I know is to implement the keyDown method in your SKScene (and not directly in the AppDelegate). You don't have to subclass anything.
- (void)keyDown:(NSEvent *)event {
[self handleKeyEvent:event keyDown:YES];
}
- (void)keyUp:(NSEvent *)event {
[self handleKeyEvent:event keyDown:NO];
}
Then use the method handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp to check which key has been pressed :
- (void)handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp {
// First check the arrow keys since they are on the numeric keypad.
if ([event modifierFlags] & NSNumericPadKeyMask) { // arrow keys have this mask
NSString *theArrow = [event charactersIgnoringModifiers];
unichar keyChar = 0;
if ([theArrow length] == 1) {
keyChar = [theArrow characterAtIndex:0];
switch (keyChar) {
case NSUpArrowFunctionKey:
self.defaultPlayer.moveForward = downOrUp;
break;
case NSLeftArrowFunctionKey:
self.defaultPlayer.moveLeft = downOrUp;
break;
case NSRightArrowFunctionKey:
self.defaultPlayer.moveRight = downOrUp;
break;
case NSDownArrowFunctionKey:
self.defaultPlayer.moveBack = downOrUp;
break;
}
}
}
// Now check the rest of the keyboard
NSString *characters = [event characters];
for (int s = 0; s<[characters length]; s++) {
unichar character = [characters characterAtIndex:s];
switch (character) {
case 'w':
self.defaultPlayer.moveForward = downOrUp;
break;
case 'a':
self.defaultPlayer.moveLeft = downOrUp;
break;
case 'd':
self.defaultPlayer.moveRight = downOrUp;
break;
case 's':
self.defaultPlayer.moveBack = downOrUp;
break;
case ' ':
self.defaultPlayer.fireAction = downOrUp;
break;
}
}
}
I took this code from the Apple SpriteKit Adventure game. I found it very usefull to learn SpriteKit :)
Swift (2.0) version of HeyFara's answer. I've just popped "breaks" in where you would make your actual function calls.
public override func keyDown(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: true)
}
public override func keyUp(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: false)
}
public func handleKeyEvent(event:NSEvent, keyDown:Bool){
if event.modifierFlags.contains(NSEventModifierFlags.NumericPadKeyMask){
if let theArrow = event.charactersIgnoringModifiers, keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters.characters{
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
Here is rougeExciter version of HeyFara's answer but in Swift 4.2... in case someone out there find this old post... as myself a few minutes ago :)...
public override func keyDown(with event: NSEvent) {
handleKeyEvent(event, keyDown: true)
}
public override func keyUp(with event: NSEvent) {
handleKeyEvent(event, keyDown: false)
}
public func handleKeyEvent(_ event: NSEvent, keyDown: Bool){
if event.modifierFlags.contains(NSEvent.ModifierFlags.numericPad) {
if let theArrow = event.charactersIgnoringModifiers, let keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters {
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
If you want to know whenever a key is pressed, you should subscribe to the global event manager.
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event) {
[NSEvent removeMonitor:event];
This will call the handler when ever a key is pressed.

Key Down With Multiple Controls

I understand that you use (BOOL)control:(NSControl *)control textView:(NSTextView *)inputfield doCommandBySelector:(SEL)commandSelector to detect the key for NSTextView and NSTextField that the user has pressed like the following.
- (BOOL)control:(NSControl *)control textView:(NSTextView *)inputfield doCommandBySelector:(SEL)commandSelector
{
if(commandSelector == #selector(insertNewline:) )
{
//... a key is down
return YES; // We handled this command; don't pass it on
}
else
{
return NO;
}
}
My question is how you tell under which text field a key is down when you have multiple such controls. I've set a tag like the following to see if a key is down for a particular text field, but it doesn't work.
- (BOOL)control:(NSControl *)control textView:(NSTextView *)inputfield doCommandBySelector:(SEL)commandSelector
{
if ([inputfield tag] == 100)
{
if(commandSelector == #selector(insertNewline:) )
{
//... a key is down
return YES; // We handled this command; don't pass it on
}
else
{
return NO;
}
}
else
{
return NO;
}
}
Thank you for your advice.
Did you wonder, why it is typed as a text view even you have a text field?
The reason for your problem is that editing is not done by the control itself, but the field editor (usually a single instance per window). You ask that field editor for its tag and will probably get the result -1. (Which means something like no tag.)
The "real" text field is the delegate of the field editor. To get it, you have to ask the parameter for its delegate. Next, you should not use a tag but set outlets to the text fields and compare the pointers. (It is a little bit tricky because of the typing.)
- (BOOL)control:(NSControl *)control textView:(NSTextView *)inputfield doCommandBySelector:(SEL)commandSelector
{
id realControl = inputfield.delegate;
if (realControl == self.field1)
{
NSLog(#"I'm 1");
return YES; // We handled this command; don't pass it on
}
else if (realControl == self.field2)
{
NSLog(#"I'm 2");
return YES; // We handled this command; don't pass it on
}
else
{
return NO;
}
}

TextView: How to listen or handle space bar key event

Okay so 'return' is 'insertNewline', I love that, but where in god's green earth is the insertSpace?
So i'm trying to invoke a spellcheck (for a keyword) after the user completes the word (aka, when they hit space bar or return ). But daddy needs to detect space-bar. Heres the textView delegate method i'm implementing...
/* listen to commands */
-(BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
{
BOOL result = NO;
if (#selector(insertNewline:) == aSelector) {
// does something
result = YES;
return result;
}
return result;
use the delegate method of UITextView as follows to achieve this...
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
// Any new character added is passed in as the "text" parameter
if ([text isEqualToString:#"\n"]) {
//code when return key pressed.
}
else if([text isEqualToString:#" "])
{
//code when space key pressed.
}
// For any other character return TRUE so that the text gets added to the view
return TRUE;
}

Detecting Ctrl + Return or Ctrl + Enter presses

I find it very hard to find anything official on this matter.
I have a TextView and override the keyDown event and try to detect if the user pressed Ctrl + Enter.
- (void)keyDown:(NSEvent *)theEvent
{
if([theEvent modifierFlags] & NSControlKeyMask && /* how to check if enter is pressed??? */)
{
NSLog(#"keyDown: ctrl+enter");
if(_delegate)
{
if([_delegate respondsToSelector:#selector(didSomething:)])
{
[_delegate performSelector:#selector(didSomething:) withObject:nil];
}
}
}else
{
[super keyDown:theEvent];
}
}
I have tried different things but nothing worked.
anyone?
(i'm sorry to ask such a trivial question but i have googled for a while now and haven't found anything)
unichar c = [theEvent charactersIgnoringModifiers] characterAtIndex:0];
if(([theEvent modifierFlags] & NSControlKeyMask) && (c == NSCarriageReturnCharacter || c == NSEnterCharacter) {
// do stuff
}
Alternatively, you can use the delegate's textView:doCommandBySelector::
- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector {
// insertLineBreak: is the method sent when a user presses control-return in a text view
if (aSelector == #selector(insertLineBreak:)) {
// do stuff
return YES;
}
}
return NO;
}
Instead of overriding -keyDown:, you could override the keyboard action (insert a line break) that’s sent when ctrl-return is typed in a text view:
- (void)insertLineBreak:(id)sender {
NSLog(#"ctrl-return");
if(_delegate)
{
…
}
}