在应用程序开发的时候有很多这样的情况,就是我想在全局使用一个快捷键然后我可以随时调用它。例如Things这个应用,你可以在窗体写自己的一些内容,如下图所示。但是更有意思的是,如果我能在全局去使用就好了,因为我们在大多数的时候,都要跳转到这个程序中,然后在程序中查询,如Things,我必须打开Things,然后添加我的内容,或者减少一个内容,如下图。
如果我能在邮件中,或者浏览器中,忽然发现这是我想看的东西,然后添加到Things中,这样多好,于是Things就出了下面这个功能。
也就是说,我可以不在应用程序窗体上进行应用程序操作。
听起来好像有点复杂,因为在操作系统中,任何程序的xx状态都是单一的,比如你不能一边魔兽一边写程序,你必须要“专注”某个应用程序来使用,比如我想发Twitter,而我又在写代码,我必须切换到Twitter客户端去写东西,而无法在当前“xx”程序的程序中去执行,非要将Twitter客户端xx,然后才能执行。
我最近写了一个Password Saver,专门用于保存非常复杂的密码,当我需要密码的时候,我也希望能够马上查询然后关闭,而不需要“xx”程序来使用,查了一些资料,下面来说说如何创建全局快捷键。
首先,这里我假设的是,你有了UI,有了Controller,一切都就绪了。应用程序会有一个主要的Controller,这里我们就叫MainController吧。我们首先需要添加一个库,以便可以注册我们的全局快捷键,我们在左侧中如下操作,Frameworks->Add->Exiting Frameworks->Carbon.framework,添加Carbon框架。添加之后,我们就有了基础设施。
然后我们需要在MainController中写如下代码。
//当这个controller绑定到view的时候,会自动调用这个方法
-(void)awakeFromNib{
//注册事件
EventHotKeyRef myHotKeyRef;
EventHotKeyID myHotKeyID;
EventTypeSpec eventType;
//注册对应的事件,如键盘按钮
eventType.eventClass=kEventClassKeyboard;
eventType.eventKind=kEventHotKeyPressed;
//注册快捷键事件
InstallApplicationEventHandler(&myHotKeyHandler,1,&eventType,self,NULL);
myHotKeyID.signature=‘mhk1‘;
myHotKeyID.id=1;
//注册EventHandler
RegisterEventHotKey(49, cmdKey+optionKey, myHotKeyID, GetApplicationEventTarget(), 0, &myHotKeyRef);
NSLog(@"awake");
}
上面的代码就是实现awakeFromNib方法,当窗体载入的时候(前提是绑定了相应窗体),会自动调用这个方法,调用的时候,我们注册了相应的事件。注意这里的RegisterEventHotKey方法,RegisterEventHotKey方法为注册快捷键的键值的方法,其中最主要的参数为(参数1)初始键,(参数2)附加键,初始键这里为空格(49),后面是command key和option key。
注册之后,我们需要写相应事件。
首先我们将如下代码添加到@implementation之前。
添加之后,我们在该类中实现上面的方法,代码如下。
NSLog(@"call hot key %@", userData);
//你想做什么事情都可以在这里做
return noErr;
}
上面的代码都是写在MainController里的,使用上面的代码就定义了全局快捷键。
但是在全局快捷键中,我们如何拿到自己的对象呢,因为这个Carbon方法是一个C方法,是无法使用self对象的,这里我们可以使用userData来得到返回的数据。可以使用MainController *controller = (MainController *)userData;来获得调用的窗体(也可以说是钩子)。
上面的获得窗体的方法,是获得本身发送窗体的对象,如果你理解了之后,或者使用了之后就会发现,就算全局快捷键使用成功,也无法xx相应的功能,这是为什么呢。首先,这个窗体是一个Window,Window是不能被后台xx的,Window只能在前台xx,也就是说你必须使用该应用程序。
在Cocoa中,我们可以使用HUD Window作为后台xx程序的入口,如下图。该类型的窗口可以独立应用程序,屏蔽其他应用程序窗口而存在在前端显示器的窗口。
我们可以创建一个新的xib文件(可以取名叫QuickWindow),并在新的xib文件中删除默认的窗口,然后拖动一个HUD窗口,然后链接。同时,我们还需要为这个窗体写一个Controller。也就是说,基本上一个窗体的view就应该对应一个Controller。
ok之后,我们可以更改一下响应的方法来调用这个窗体,代码如下(注意,这代码还是在MainController中)。
NSLog(@"call hot key %@", userData);
//打开QuickWindow
NSWindowController *add_window = [[NSWindowController alloc] initWithWindowNibName:@"QuickWindow"];
[add_window loadWindow];
//打开窗体
[add_window showWindow:[add_window window]];
[[add_window window] makeMainWindow];
return noErr;
}
上面的代码就是打开窗体的代码,另一个问题就来了。窗体打开之后,该窗体是默认不会被xx和选中的,我们需要在对应的窗体的Controller中用代码设置默认焦点。如下。
//这里的self_panel就是窗体panel
//定义变量的原因是通过在IB链接窗体,然后调用改变量就能够使用窗体
[[self self_panel] makeKeyAndOrderFront:self_panel];
}
在新的窗体中,还需要设置Panel的属性为Non Activating为True才行,当然,记得也要设置一下firstResponer(事件中拖动即可),如下。
1.首先添加Framework。
2.然后定义全局快捷键。
3.其次创建新窗口xib和controller。
4.然后修改快捷键响应方法,打开新窗口。
5.给新窗口设置默认焦点。