I have a Cocoa application that captures keypresses through a custom view in the view hierarchy. This view implements the keyUp and keyDown methods, and the keypresses are received. Even so, Cocoa still insists on playing the system error sound/ding every time I press a key. Any solutions?
Note: Although I tried to make this view first responder, it didn't work. That may have something to do with it.
If you have unsuccessfully tried to make the view the first responder, it's most likely because NSView returns NO for acceptsFirstResponder. You can have your NSView subclass override acceptsFirstResponder to return YES:
- (BOOL)acceptsFirstResponder {
return YES;
}
That should eliminate the beeps. Alternatively, you could have the NSView subclass override NSResponder's performKeyEquivalent: method to return YES, which should also eliminate the NSBeeps:
- (BOOL)performKeyEquivalent:(NSEvent *)event {
return YES;
}
UPDATE:
Not sure what to suggest. I actually wrote a "Keyboard Cleaner Helper" app that's designed to basically do something similar to what you want. (I used it on my laptop when I wanted to clean the keyboard and didn't the hundreds of key presses to randomly rename files or result in repeated error beeps).
Sample project: http://www.markdouma.com/developer/KeyboardCleanerHelper.zip
Running that app, I can't get it to beep at all (notice calls are logged to Console).
Related
To be specific I would like to receive notifications when an NSTextField gets focus, or when the user clicks on it and is about to start editing.
I've tried the textDidBeginEditing: method but this is only fired when the user actually starts to type and I need it when the text field becomes the first responder.
I've tried overriding -(BOOL)becomeFirstResponder but that's only called when the text field first becomes the first responder and not any time after that.
I've tried registering for NSControlDidBeginEditing notifications but never got any.
Is this possible?
Implement the window's delegate method windowWillReturnFieldEditor:toObject:. This tells you that the field editor is switching to a different object. Look to see if this object is the text field.
Alternatively, subclass NSWindow and override makeFirstResponder:. Call super, look to see what responder is becoming first responder and whether the call to super returned YES, and return the result of the call to super.
Just to be clear #matt's answer put me on the right path; I thought I just ought to clarify exactly how you can do this.
Aim
So I wanted to have an NSTextField subclass that would know when it became active (i.e. first responder), and then notify it's delegate.
Problem
It turns out the under the hood of OS X text editing is a messy world and you can't really rely on NSTextField to do it all. Basically when an object that is involved in text editing becomes the first responder, something (the window, the system, the NSApplication) gives it an _NSKeyboardClipView (I think it's called that...) as a subview. In turn the _NSKeyboardClipView has an NSTextView as a subview, and it's the NSTextView that becomes the first responder.
Solution
• Subclass (or extend) NSWindow and override the makeFirstResponder: method.
• Fire a notification using NSNotificationCenter who's object is the responder object that is passed to the makeFirstResponder: method.
• Catch the notification in your NSTextField subclass.
• Here's the horrible bit: you need to check that the notification.object is, a) a subclass of NSView and, b) the notification.object.superview.superview == self (you will have to cast to NSView here because the object will be of type id). e.g:
- (void)didBecomeFirstResponder:(NSNotification *)note
{
if ([note.object isKindOfClass:[NSView class]] && [[(NSView *)note.object superview] superview] == self) {
// You just became the first responder
}
}
It's horrible and tacky/hacky but it does work.
Use to, this type of syntax was needed in the "Did End On Exit" action of a UITextField:
[self.myTextField resignFirstResponder];
At least in the simulator of Xcocde 5, that code seems to be no longer needed. The keyboard will hide when you press the return button and there is no associated action attached. Is that correct or is this simulator only behavior?
I guess this code is still needed for iOS6?
Actually, the premise of your question is wrong. Nothing has changed, except that you've discovered the truth. It was never necessary to say resignFirstResponder if a text field targeted its "did end on exit" action: the mere fact of the targeting caused (and still causes) the Return key to perform dismissal automatically.
In fact, the "did end on exit" action doesn't actually have to do anything; no corresponding method need be implemented. And the target need not be a real object; it can be nil. In other words, this code will endow any text field with the "magical" property that it automatically dismisses the keyboard when the Return key is pressed:
[textField addTarget:nil action:#selector(dummy:)
forControlEvents:UIControlEventEditingDidEndOnExit];
That works (and has worked since as early as I've been using iOS, i.e. iOS 3) even though the target is nil and the action method is never implemented anywhere (there is no dummy: method in the app). It always surprises me that this important technique is not more widely known.
You still need it. To close a keyboard I make an action called "close keyboard" and connect that to my text field or text view. Here is the code...
-(IBAction)closeKeyboard {
[textfield1 resignFirstResponder];
}
I'm almost done writing an image editing program, but ran into a problem that should have a simple solution.
Basically, I've built a set of buttons and NSTextFields and a Custom View into the main xib, just dropping them straight off the Library into the default window (and then, of course, linking them up with IBOutlets and IBActions). One of the buttons is an "Open" button that calls a function. The function does several things: it runs a NSOpenPanel, and then changes some of the NSTextFields (used for changing image name and path). That code is called as follows:
- (IBAction)openButtonPressed: (id)sender {
[self runOpenPanel];
}
Now I also happen to be running my keyDown handler from the Custom View, as I've told it to acceptFirstResponder. (I know, it's probably bad practice to not write a separate Controller class, but that's best left for another time.) So my keyDown event looks like this (simplified, as in the actual code I have has if statements to handle certain keys separately that don't pertain to this question):
- (void)keyDown: (NSEvent *)theEvent {
[self runOpenPanel];
}
So they both use [self runOpenPanel] but from different contexts. The problem I'm having is that "runOpenPanel" makes a few calls to change IBOutlets, like this:
-(void)runOpenPanel {
// Omitting some of the trivial NSOpenPanel code
[myTextField setStringValue: #"The file name NSString from the aforementioned omitted code."];
}
So myTextField updates when the function is run by an Interface Builder button, but not when run by the Custom View's keyDown handler. Is there some way to call runOpenPanel that will allow it to access myTextField? I've tried using [[super self] runOpenPanel] (don't laugh) and a number of other things. Thanks in advance!
I have created a fairly simple screensaver that runs on Mac OS 10.6.5 without issue.
The configuration screen has accumulated quite a few different options and I'm trying to implement my own preview on the configureSheet window so the user (just me, currently) can immediately see the effect of a change without having to OK and Test each change.
I've added an NSView to the configureSheet and set the custom class in Interface Builder to my ScreenSaverView subclass. I know that drawRect: is firing, because I can remove the condition for clearing the view to black, and my custom preview no longer appears with the black background.
Here is that function (based on several fine tutorials on the Internet):
- (void)drawRect:(NSRect)rect
{
if ( shouldDrawBackground )
{
[super drawRect:rect];
shouldDrawBackground = NO;
}
if (pausing == NO)
[spiroForm drawForm];
}
The spiroForm class simply draws itself into the ScreenSaverView frame using NSBezierPath and, as mentioned, is not problematical for the actual screensaver or the built-in System Preferences preview. The custom preview (configureView) frame is passed into the init method for, um, itself (since its custom class is my ScreenSaverView subclass.) The -initWithFrame method is called in configureSheet before returning the configureSheet object to the OS:
[configureView initWithFrame:[configureView bounds] isPreview:YES];
Maybe I don't have to do that? It was just something I tried to see if it was required for drawing.
I eventually added a delegate to the configureSheet to try triggering the startAnimation and stopAnimation functions of my preview via windowWillBeginSheet and windowWillEndSheet notifications, but those don't appear to be getting called for some reason. The delegate is declared as NSObject <NSWindowDelegate> and I set the delegate in the configureSheet method before returning the configureSheet object.
I've been working on this for days, but haven't been able to find anything about how the OS manages the ScreenSaverView objects (which I think is what I'm trying to emulate by running my own copy.)
Does anybody have any suggestions on how to manage this or if Apple documents it somewhere that I haven't found? This isn't really required for the screensaver to work, I just think it would be fun (I also looked for a way to use the OS preview, but it's blocked while the configureSheet is activated.)
OK, there are a couple of 'duh' moments involved with the solution:
First of all, I was setting the delegate for the sheet notifications to the sheet itself. The window that the sheet belongs to gets the notifications.
Secondly, that very window that the sheet belongs to is owned by System Preferences, I don't see any way to set my delegate class as a delegate to that window, so the whole delegate thing doesn't appear to be a viable solution.
I ended up subclassing NSWindow for the configureSheet so that I could start and stop animation on my preview by over-riding the makeKeyWindow and close methods.
- (void) makeKeyWindow
{
if (myPreview != nil)
if ( ! [myPreview isAnimating])
{
[myPreview startAnimation];
}
[super makeKeyWindow];
}
I also had to add an IBOutlet for my preview object itself and connect it in Interface Builder.
Still working out a couple of issues, but now when I click on my screensaver Options button, my configureSheet drops down and displays its own preview while you set options. Sheesh. The hoops I jump through for these little niceties. Anyway, I like it. Onward and upward.
Just wondering, how I go about detecting different keystrokes, and then detecting what key has been pressed I tried using this,
-(void)keyDown:(NSEvent *)event
but didnt seem to get any results. I've also had a search around but didn't find anything. I'm guessing I may have to set up something in interface builder to detect keystrokes?
I also think that it has something to do with what is selected, if its a text field something.
keyDown: method is called only for certain view and it's subviews I think. If you need all keystrokes for your app - check NSEvent class method:
+ (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask
handler:(NSEvent* (^)(NSEvent*))block
Read upon in it Xcode documentation. I presume you're on snow leopard.
I tried using this, - (void)keyDown:(NSEvent *)event but didnt seem to get any results.
What do you mean “using” it?
You need one of your objects to respond to that message. That means you need it to be a responder, and to be in the responder chain whenever it is appropriate for the keystrokes it handles to be pressed.
Depending on what the keypress does, it may be appropriate for a single custom view to handle it; if not, it should probably be the window controller that handles it. Either one should already be in the responder chain at the appropriate times. Whichever way you go, you'll need to subclass either NSView (for a custom view) or NSWindowController.