Check modifierFlags of NSEvent if a certain modifier was pressed but no other - objective-c

I just experimented with the addLocalMonitorForEventsMatchingMask:handler: method in NSEvent and came across the following question: How do I find out if only certain modifiers were pressed?
A short example to set this question into context: I wanted to listen for the shortcut "⌘+W". Therefore I wrote the following code:
[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *theEvent) {
if ([theEvent modifierFlags] & NSCommandKeyMask && [theEvent keyCode] == 13) {
[self.window performClose:self];
}
return theEvent;
}];
This works well, however the shortcut will be triggered, even if more modifier keys are pressed, e.g. "⌃+⌘+W" or "⇧+⌃+⌥+⌘+W". Is there a way to circumvent this?
A simple solution would be to check for all other modifier keys and ensure they are not pressed. This seems tedious and error prone - besides it's ugly enough as it is now with the unary "&". In addition you may get into trouble if (for some reason) another modifier key is added to keyboard layouts.
As always I'm thankful for any recommendations.

I think this'll do it:
// Mask out everything but the key flags
NSUInteger flags = [theEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if( flags == NSCommandKeyMask ){
// Got it!
}
Hat tip to SpaceDog for pointing out the deprecation of the original mask name, NSDeviceIndependentModifierFlagsMask.

Swift 5 version
if event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .command {
// Got it!
}

#JoshCaswell answer has been outdated thanks to Apple, because NSDeviceIndependentModifierFlagsMask has been deprecated since 10.12.
His answer updated to the new syntax is
// Mask out everything but the key flags
NSUInteger flags = [theEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if( flags == NSCommandKeyMask ){
// Got it!
}
NSDeviceIndependentModifierFlagsMask has been replaced with NSEventModifierFlagDeviceIndependentFlagsMask because it makes a world of difference...

Maybe even better...
if event.modifierFlags.contains(.shift){
// do it
}

Related

Command-Key-Up Cocoa

I'm trying to mimic the functionality of the cmd-tab keyboard shortcut where the user can switch between applications hitting a certain key and then when they release command something happens.
I'm using this code right now but it can only detects keydown. I need this to fire on key up
- (void)flagsChanged:(NSEvent *)theEvent {
if ([theEvent modifierFlags] & NSCommandKeyMask) {
NSLog(#"Do my stuff here");
}
}
Thanks
According to the docs:
Informs the receiver that the user has pressed or released a modifier
key (Shift, Control, and so on).
What you need to do here is when you get the event in which the command key goes down, you need to set a flag somewhere, and in subsequent calls, check for the absence of the command key being down.
For instance, assuming you have an ivar called _cmdKeyDown:
- (void)flagsChanged:(NSEvent *)theEvent
{
[super flagsChanged:theEvent];
NSUInteger f = [theEvent modifierFlags];
BOOL isDown = !!(f & NSCommandKeyMask);
if (isDown != _cmdKeyDown)
{
NSLog(#"State changed. Cmd Key is: %#", isDown ? #"Down" : #"Up");
_cmdKeyDown = isDown;
}
}

Capture Copy&Paste Key

I'm working on an OpenGL game in Cocoa, and need to capture when the user attempts to copy or paste (via command+c, or command+v).
So far, I have an NSView<NSTextInputClient> as the first-respondant of my NSWindow. It is successful in allowing non-ASCII characters to be typed into my game (中文維基百科, for example) but I'm lost trying to capture Copy&Paste.
I think I might be able to get a working solution using flagsChanged combined with keyDown, but this feels like a hack, and I'm sure someone out there knows a better solution. :)
Edit: For clarity, I'm looking mainly for the existence of a predefined Copy event or keycode. The rationale being that if I manually define Copy as "Command+C" then that might break if a user has remapped his/her keys or is using some accessibility tool.
You can use the NSEvent class method: + (id)addGlobalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(void (^)(NSEvent*))block
or you can implement - (void)keyDown:(NSEvent *)theEvent
- (void)keyDown:(NSEvent *)theEvent {
if ([theEvent modifierFlags] & NSCommandKeyMask) {
NSString *character = [theEvent charactersIgnoringModifiers];
if ([character isEqualToString:#"c"]) {
NSLog(#"Capture Copy&Paste Key");
}
}
[super keyDown:theEvent];
}

Parse Issue: Expected Expression (Objective C)

For what ever reason my xcode has decided it doesn't like me... I'm getting the error stated in the title on this line
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger row = [_tableView selectedRow];
if (row == –1) //<---- this line
{
return;
}
NSString *selectedVoice = [_voices objectAtIndex:row];
[_speechSynth setVoice:selectedVoice];
NSLog(#"new voice = %#", selectedVoice);
}
I do believe that it has something to do with _tableView being befuddled because when I attempted to get the IDE to help me to type (you know when it guesses what you might what to finish your word with by doing an API lookup of available functions) it doesn't show selectedRow as a possibility :(
incase it's needed i've put the .m and .h in a pastebin to save some space on your screens... FYI I'm following the Coca Programming for Mac OSX fourth Edition chapter 6.10
In your line
if (row == –1)
the minus-sign is not the real minus-sign, but an "EN DASH" (Unicode U+2013). Perhaps you accidentally pressed the option-key together with the minus-key when typing that code.
Replacing that character with a minus-sign fixes the problem.
UITableView doesn't have a method called selectedRow.
Perhaps you should be using:
- (NSIndexPath *)indexPathForSelectedRow

buttonTitleAtIndex not working with iOS6

I just finished updating an app for support for iPhone 5 and iOS6 and I found an odd bug.
in iOS5 this code works fine, but on every iOS6 device nothing happens when the buttons are pressed and the debugger sees buttonTitleAtIndex:buttonIndex as an unknown method. I made a work around using the actual buttonIndex number instead of the string, but I'm confused why this would happen, as I know of no reason why support for this should change with iOS6.
if([actionSheet buttonTitleAtIndex:buttonIndex] == #"Email to a friend") {
NSLog(#"Email");
[self displayComposerSheet];
}
else if ([actionSheet buttonTitleAtIndex:buttonIndex] == #"Add to Favorites") {
NSLog(#"favorites");
[self addFavorite];
}
else if ([actionSheet buttonTitleAtIndex:buttonIndex] == #"Post to Facebook") {
NSLog(#"Facebook");
[self shareOnFacebook];
}
else if ([actionSheet buttonTitleAtIndex:buttonIndex] == #"Tweet this") {
NSLog(#"tweet");
[self tweet];
}
You cannot compare Objective-C objects by address. == will not work the way you want it to. That it was working for you on iOS 5 was a quirk only, and could change with a simple recompile.
You must use -isEqual: to compare two objects:
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqual:#"Email to a friend"]) {
...
}
A better approach would be to compare the button index to a known list (an enum for instance, however. This will make localization easier and prevent bugs if you happen to rename a button later.
The following prints correct title for me:
NSLog(#"TITLE: %#", [actionSheet buttonTitleAtIndex: buttonIndex]);
For comparison, do like following:
[[actionSheet buttonTitleAtIndex: buttonIndex] isEqualToString: #"Email to a friend"]
It might be because you are not using isEqualToString. "==" is not really doing a string comparison, it's simply comparing the same spot in memory, which may be the reason it's failing in iOS 6.

Cocoa Touch - Keyboard Capturing

How can I tell what keys the user pressed into a textView?
And before you ask since it sounds similar to a keylogger, I'm making a typing app and I need to know if what they entered of the correct matching key to what they were prompted.
Thanks!
You should set the delegate of the UITextView to one of your classes. (in IB or programmatically, does not matter)
In your delegate, you can put the following function, or something similar:
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
if ( [text length] == 0 ) return YES; // always allow deletion of characters
NSString *new = [textView.text stringByReplacingCharactersInRange:range
withString:text];
if ( [new length] > 100 ) // PUT IN YOUR MAGIC CONDITION HERE
{
return NO; // don't allow the edit to happen
}
return YES; // by default, allow the edit to happen
}
this will only limit input to 100 chars, but you can make it as complicated as you see fit.
edit ps, you asked "what key the user pressed", but since we also have copy&paste and auto-correction, this may give a text which is longer than 1 char!