Is it possible to run a method every time the user presses a key. Basically I want to run a sound like on the iPhone or iPad when a key is pressed. I do not want to detect key presses in my window or in a certain control, I want to detect ALL presses (such as when they are typing in Safari or something. I do not need to know what the key is.
Thanks
Use CGEventTapCreate documented here:
https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html
Or use NSEvents addGlobalMonitorForEventsMatchingMask:handler: documented here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nsevent_Class/Reference/Reference.html
NSEvent Example:
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
[self keyWasPressedFunction: event];
//Or just put your code here
}];
I would say NSEvents are easier...
Note:
For security reasons, Apple requires you have "Enable access for assistive devices" turned on in System Preferences, in order to use ether of the above methods.
You can get pretty close with a Quartz event tap, but some keypresses aren't detectable even with one for the sake of security.
If you tell us the broader goal you have in mind, we can suggest alternatives. Are you trying to establish a global hotkey for your app? Are you writing a keylogger or malware? What?
Use NSEvents addGlobalMonitorForEventsMatchingMask:handler:
In applicationDidFinishLaunching add the following code, build & go!
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
NSLog(#"%#", event.characters);
}];
Apple requires you have "Enable access for assistive devices" turned on in System Preferences.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nsevent_Class/Reference/Reference.html
Related
I am writing a game-like app for OS X and need to know if the left and right arrows are pressed during the game loop. I also check if the space bar is down. For this I implement
-(void)keyDown:(NSEvent *)event;
-(void)keyUp:(NSEvent *)event
in my view and store the state of each key in some flags. I also check if the command button is pressed using:
-(void)flagsChanged:(NSEvent *)event;
If the window isn't main (another window gets focus) or resigns key (spotlight opens) I reset the flags. I do this by implementing:
-(void)windowDidBecomeKey:(NSNotification *)notification;
-(void)windowDidResignKey:(NSNotification *)notification;
-(void)windowDidResignMain:(NSNotification *)notification;
-(void)windowDidBecomeMain:(NSNotification *)notification;
This works almost all the time. If I press command and then space, spotlight opens and my app resigns key. However, if I hold for example the left arrow key, and first presses and holds space, then command, spotlight won't show up, but I sometimes lose the key-up event for the arrow key when I release them. It doesn't happen every time but (could depend on the release order) but it is easily reproduced in a few tries. So the key gets stuck down until the next press.
Is there another state my app enters that I'm not aware of? If not, this approach seems a bit fragile.
Is there a more robust way of checking that a key is down (that doesn't require installation of a logger tool or enabling accessibility for the app in preferences)?
For this purpose I use NSEvent's addLocalMonitorForEventsMatchingMask:handler:-static method. It works while the app is active. Use addGlobalMonitor... for handling global events but be aware your app may be rejected from the AppStore. Here some code sample.
id monitor=[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:(NSEvent *)^(NSEvent *theEvent){
if (theEvent.keyCode==/*your key code*/) // you should check the key modifiers too
{
// your code here
}
return theEvent; // you may return the event to pass the key to the receiver or nil if no need
}];
// remove monitor
[NSEvent removeMonitor:monitor];
I'm writing a Cocoa app that needs to be able to capture keyboard events even when not focused. (It's controlling another app via the Apple Scripting Bridge).
I have tried the solution here: OSX: Detect system-wide keyDown events?
It compiles fine, but doesn't actually do anything. I'm putting the code in my init method.
I also can't get CGEventTap to work either. Any suggestions?
Here's my code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
NSLog(#"test");
}];
}
Also, I'm aware that assistive devices needs turned on. Unfortunately when I go into the settings it only lists specific apps. Mine isn't one of them.
Use AXIsProcessTrustedWithOptions to request access. Then you'll show up in System Preferences.
I'm trying to figure out a way to give a user feedback when they have saved settings. similar to Microsoft's "File Saved" dialog Is there a class for this type of dialog? I do not want to require any action by the user. Just "Your setting have been saved" then disappears after a short delay. Maybe a better way to describe would be like a jQuery message box with a fade in fade out type thing
Is there a class for this type of dialog?
That isn't a "dialog", because you're not accepting input from the user. At best, it's an alert, and you could therefore use NSAlert (see also "Dialogs and Special Panels") however, what you are contemplating is contrary to the recommendations given in the HIG for "Alerts":
Avoid using an alert merely to give users information. Although it’s important to tell users about serious problems, such as the potential for data loss, users don’t appreciate being interrupted by alerts that are informative but not actionable. Instead of displaying an alert that merely informs, give users the information in another way, such as in an altered status indicator.
In other words, this probably wouldn't be considered a good user experience by the OS X-using population.
You can still do this, if you absolutely must, by creating a sheet or alert window and setting a timer to dismiss it.
A much better plan would be to have a label somewhere in your interface whose text could display this information, again using a timer to clear the notice after an appropriate duration.
Yet another option (possibly the best) would be to put this notice somewhere that the user only sees it upon request. The HIG mentions Mail.app's information area at the bottom of its sidebar, for example.
It is simple to fade a window in and out using the NSViewAnimation see also NSAnimation Class
An example I use something like this.
- (void)fadeWindowIn{
//--make sure the window starts from 0 alpha. Or you may get it jumping in view then try and fade in.
[theWindow setAlphaValue:0.0];
//-- set up the dictionary for the animation options
NSDictionary *dictIn = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeWindowIntAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictIn]] autorelease];
[fadeWindowIntAnim setAnimationCurve:NSAnimationLinear];
[fadeWindowIntAnim setDuration:2];
[fadeWindowIntAnim setFrameRate:20.0];
//--start the animation
[fadeWindowIntAnim startAnimation];
//--set the timer for the fade out animation
[NSTimer scheduledTimerWithTimeInterval:4.8 target:self selector:#selector(fadeWindowOut) userInfo:nil repeats:NO];
}
-(void)fadeWindowOut{
//-- fade the window.
NSDictionary *dictOut = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeOutAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictOut]] autorelease];
[fadeOutAnim setAnimationCurve:NSAnimationLinear];
[fadeOutAnim setDuration:1.2];
[fadeOutAnim setFrameRate:20.0];
[fadeOutAnim startAnimation];
}
theWindow is the NSWindow or NSView you want to fade in and out. Read the references to understand the options.
You can create your own such popup (using NSTimer to dismiss as needed), but perhaps an easier way would be to use the existing third-party library at http://code.google.com/p/toast-notifications-ios/. This library emulates Android's "toast" functionality.
Note that this library is for iOS development (not OSX), but wasn't sure which platform you were planning to target. Regardless, it should be adaptable with a little work.
The other answers about timers and such cover that aspect of it pretty well. I just wanted to jump in and suggest you take a look at the Growl framework. This seems to be the preferred way to do this sort of passive notification until Apple builds it into the OS.
Among other things, it gives the user a lot of control over how the notifications look, where they live on the screen, how long they stay up, and which apps are even allowed to display them. And they do this without you having to write any code. The downside is that it's another thing for your users to have to install, which could be a deal breaker for your app.
They also recently moved into the App Store and started charging a nominal fee ($2 or $3, I think) which could be seen as a downside but I think of it as a more positive thing: users will have a much easier time installing it now.
Some apps that make use of Growl notifications include BBEdit, Transmission, Scrivener, Twitteriffic, etc. Which is to say that it's not a fly-by-night thing.
As a user, I hate it when apps try to roll their own notifications since I lose all of the control that I get with Growl.
Just a thought, anyway.
I'm writing a plugin for an application. I cannot derive from NSApplication
as it is a third party application. I can get the callback in my plugin when any key is pressed. But I will not know what key is pressed. So is there any call in Cocoa to find the last key pressed when I get the callback? I only have NSView object.
Any ideas will help me a lot.
Thanks,
Dheeraj.
A couple of thoughts:
Use [NSApp currentEvent]. I know you don't think you have an NSApplication instance, but you should try this. It might work.
Do some event monitoring in your plugin (CGEventTap, NSEvent local monitor, etc) and record whenever you see a keypress event.
I have asked a similar question here and got some answers, so first of all sorry for making you people bother once again.
But I have an argument this time. First I will show my piece of code
- (void) showTheAlert{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Hey!" message:#"?" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Yes",#"No",#"Don't know eaxactly.",nil];
[alertView setTag:101];
[alertView show];
}
- (void)willPresentAlertView:(UIAlertView *)alertView{
if(alertView.tag == 101){
[[[alertView subviews] objectAtIndex:2] setBackgroundColor:[UIColor colorWithRed:0.5 green:0.0f blue:0.0f alpha:0.5f]];
[[[alertView subviews] objectAtIndex:3] setBackgroundColor:[UIColor colorWithRed:0.0 green:0.5f blue:0.0f alpha:0.5f]];
}
}
And my final alert looks like
Now my confusion is that, [alertView subviews] is not documented as some people may say but alertview is a subclass of UIView, which has a property called subviews.
So I am using the documented property of a superclass which is definitely allowed.
So if this alertview may cause rejection of my app or not? ( I don't think apple will have any base to say I am using the undocumented or a private api. The look and feel is also alike to alertview.)
Apples iPhone Human Interface Guidelines about alert views clearly states:
The infrequency with which alerts appear helps users take them seriously. Be sure to > minimize the number of alerts your application displays and ensure that each one offers > critical information and useful choices. In general, try to avoid creating alerts that:
Update users on tasks that are
progressing normally.
Instead, consider using a progress
view or an activity indicator to
provide progress-related feedback to
users (these controls are described
in “Progress Views” and “Activity
Indicators”).
Ask for confirmation of
user-initiated actions.To get confirmation for an action the user initiated, even a potentially risky action such as deleting a contact, you should use an action sheet (described next in “Using Action Sheets”).
Inform users of errors or problems
about which they can do nothing.
Although it might be necessary to use an alert to tell users about a
critical problem they can’t fix, it’s
better to integrate such information
into the user interface, if possible.
For example, instead of telling users
every time a server connection fails,
display the time of the last
successful connection.
So, my advice, the time waiting for a potential rejection isn't worth your time. Don't risk it.
To follow on Henrik's reply, in the iPhone Human Interface Guidelines section "Designing an Alert", they say the following:
Although you can choose the number of
buttons to place in an alert, a
two-button alert is often the most
useful, because it is easiest for
users to choose between two
alternatives. It is rarely a good idea
to display an alert with a single
button because such an alert cannot
give users any control over the
situation; instead, it can only
display information and provide a
dismiss button. An alert that contains
three or more buttons is significantly
more complex than a two-button alert,
and should be avoided if possible. In
fact, if you find that you need to
offer users more than two choices, you
should consider using an action sheet
instead (see “Using Action Sheets” and
“Designing an Action Sheet” for more
information on this type of view).
Because users sometimes respond to
alerts without reading them carefully,
be sure to provide an appropriate
default choice. To help guide
inattentive users towards this choice,
make the light-colored, right-hand
button the safe, default alternative.
For example, you might choose to make
this button the Cancel button, to help
users avoid inadvertently causing a
dangerous action, or you might make it
represent the most common response, if
the resulting action isn’t
destructive.
The following guidelines describe how
buttons are configured in an alert:
In an alert with two buttons, the button on the left is always
dark-colored and the button on the
right is never dark-colored.
In a two-button alert that proposes a potentially risky action, the button
that cancels the action should be on
the right and light-colored.
In a two-button alert that proposes a benign action, the button that
cancels the action should be on the
left (and therefore dark-colored).
In an alert with a single button, the button is light-colored.
You are clearly violating the guidelines in size, shape, number, and color of the buttons in your alert view (red has a very clear meaning as a destructive action, not a confirmation). Even if Apple doesn't reject your application in review (which they tend to do for clear violations of the Human Interface Guidelines), this would be extremely confusing to your users.
Also, navigating the hidden view hierarchy for any Apple-supplied user interface element is a very bad practice. The view hierarchies are undocumented, and do change often. Many of the applications that started crashing when people upgraded to iPhone OS 3.0 did so because those applications did something funky with subviews of UI elements, and those elements changed in the new OS version. Apple even specifically called this out in the iPhone OS 3.0 migration documents (which I can't find now).
Because of the problems this caused, they appear to have cracked down on this practice and have been rejecting applications because of it. Even if they don't, it shows contempt for your users if you do this, because it means that you don't care if your application breaks with future OS upgrade.
I'm fairly sure altering UIAlertView by digging through the view hierarchy is a no-no. Firstly because it "uses standard iPhone screen images in a non-standard way, potentially resulting in user confusion", and secondly because if they change the view hierarchy your app is broken.
I might be wrong, I've never tried to get something like this onto the store, but my gut says don't risk it.
You can get a red button using a standard UIActionSheet, can you not?