Reputation: 1057
I am making an app that will add sound to keypresses as the user types in an NSTextField
. I need to capture keystrokes and know what each individual keypress is (like "d" or "space" or "6"). The app depends on this. There is no other way around it.
Each window is an NSDocument
File Owner, and it has a single NSTextField
in it, which is where the document data is parsed, and the user will type.
After hours of parsing the Internet for answers and hacking away at code, the four most commonly repeated answers are:
control:textView:doCommandSelector:
" that doesn't give me individual keys, and some keys need their own unique sound trigger.controlTextDidChange:
or textView:shouldChangeTextInRange:replaceString:
" controlTextDidChange doesn't give me individual keys, and the second one only works for textViews or UIKit.UIKit
instead of AppKit
, which is iOS-only. The weird thing is that if I subclass NSTextField
, it receives -keyUp
. I don't know where -keyDown
is going.
So my ultimate question is: can you tell me some kind of step-by-step way to actually capture the keyDown that is sent to NSTextField
? Even if it's a hack. Even if it's a terrible idea.
I would love to solve this problem! I am very grateful for your reading.
Upvotes: 5
Views: 2147
Reputation: 2554
This is a pretty old question, but as I was trying to implement a NSTextField that could react to keyDown so that I could create a hotkey preferences control I found I wanted the answer to this question.
Unfortunately this is a pretty non-standard use and I didn't find any places that had a direct answer, but I've come up with something that works after digging through the documentation (albeit in Swift 4) and I wanted to post it here in case it helps someone else with a non-standard use case.
This is largely based off of the information gleaned from the Cocoa Text Architecture Guide
There are three components to my solution:
Creating your NSWindowController
and setting a NSWindowDelegate
on your NSWindow
:
guard let windowController = storyboard.instanciateController(withIdentifier:NSStoryboard.SceneIdentifier("SomeSceneIdentifier")) as? NSWindowController else {
fatalError("Error creating window controller");
}
if let viewController = windowController.contentViewController as? MyViewController {
windowController.window?.delegate=viewController;
}
Your NSWindowDelegate
class MyViewController: NSViewController, NSWindowDelegate {
// The TextField you want to capture keyDown on
var hotKeyTextField:NSTextField!;
// Your custom TextView which will handle keyDown
var hotKeySelectionFieldEditor:HotKeySelectionTextView = HotKeySelectionTextView();
func windowWillReturnFieldEditor(_ sender: NSWindow, to client: Any?) -> Any? {
// If the client (NSTextField) requesting the field editor is the one you want to capture key events on, return the custom field editor. Otherwise, return nil and get the default field editor.
if let textField = client as? NSTextField, textField.identifier == hotKeyTextField.identifier {
return hotKeySelectionFieldEditor;
}
return nil;
}
}
Your custom TextView where you handle keyDown
class HotKeySelectionTextView: NSTextView {
public override func keyDown(with event: NSEvent) {
// Here you can capture the key presses and perhaps save state or communicate back to the ViewController with a delegate pattern if you prefer.
}
}
I fully admit that this feels like a workaround somewhat, but as I am experimenting with Swift at the moment and not quite up to speed with all of the best practices yet I can't make an authoritative claim as to the "Swift-i-ness" of this solution, only that it does allow a NSTextField to capture keyDown events indirectly while maintaining the rest of the NSTextField functionality.
Upvotes: 2
Reputation: 139
controlTextDidChange is quite a good solution, but don't forget this 2 important things:
If you miss those points, you will have no result.
Upvotes: 3
Reputation: 22707
Text editing for an NSTextField
is handled by an NSTextView
provided by the window, called the field editor. See the NSWindow
method fieldEditor:forObject:
and the NSWindowDelegate
method windowWillReturnFieldEditor:toObject:
. I suppose you could use one of these to provide your own subclassed NSTextView
as the field editor. Or, could you simply use NSTextView
instead of NSTextField
?
Upvotes: 0
Reputation: 15015
Try like this if you print nslog you will get individual character record for example you pressd "A" you will get the same in console:-
-(void)controlTextDidChange:(NSNotification*)obj
{
NSLog(@"%@",[yourTextfield stringValue]);
}
Also, not sure this is only your requirement.
Upvotes: 0