2011年12月30日金曜日

ホットキーを登録する

ホットキーについて調べてみると Carbon フレームワークを使用して登録するというのが常套手段のようだ。

しかし、今更 Carbon か、、、という感は否めない。


そこで探してみると、 NSEvent のメソッドがあった。


Installs an event monitor that receives copies of events posted to other applications.
+ (id)addGlobalMonitorForEventsMatchingMask:(NSEventMask)mask
                                    handler:(void (^)(NSEvent*))block


Installs an event monitor that receives copies of events posted to this application before they are dispatched.
+ (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask
                                   handler:(NSEvent* (^)(NSEvent*))block


addGlobalMonitorForEventsMatchingMask:handler: は
他のアプリケーションに対して送られたイベントを処理するためのメソッドで、
addLocalMonitorForEventsMatchingMask:handler: は
自アプリケーションに対して送られたイベントを処理するためのメソッドのようだ。

戻り値として、 eventMonitor オブジェクトが返却されるので不要になった際に必ず登録を解除してやる必要がある。
+ (void)removeMonitor:(id)eventMonitor

Carbon を使ったやり方だと NSApplication クラスのサブクラス化など必要で手間が多かったのだが
これなら簡単に実装できそうだ。

参考リンク
NSEvent Class Reference (Mac OS X Developer Library)

2011年11月12日土曜日

UIAlertView で block を使用する

iOS 5 時点の UIAlertView では delegate を設定し、完了後の処理を行っている。
@implementation MyClass
- (void)showAlert
{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Title"
                                                        message:@"Message"
                                                       delegate:self
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:@"Other Button", nil];
    [alertView show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    // Do something.
}
@end


これを GCD みたいに block を使用して処理したいと思ったので調べてみたところ、実装できたので公開する。


まず UIAlertView をサブクラス化し、インスタンス変数 _completionHandler を追加する。
buttonIndex は押下されたボタンを引数として渡すためのもの。
@interface MyAlertView : UIAlertView {
    void    (^_completionHandler)(NSInteger buttonIndex);
}
- (void)showWithCompletionHandler:(void(^)(NSInteger buttonIndex))_completionHandler;
@end


実装は次のとおり。
@implementation MyAlertView
- (void)showWithCompletionHandler:(void(^)(NSInteger buttonIndex))completionHandler
{
    // completionHandler を copy し、アラートを表示する。
    _completionHandler = [completionHandler copy];
    [self show];
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
    // アラートを閉じ、 _completionHandler を実行する。
    [super dismissWithClickedButtonIndex:buttonIndex animated:animated];
    _completionHandler(buttonIndex);
}
- (void)dealloc
{
    // _completionHandler を解放する。
    [_completionHandler release];
    [super dealloc];
}
@end


通常、 block をコピーする必要はないが、今回はその必要がある。
通常は、ブロックをコピー(または保持)する必要はありません。ブロックの宣言を含むスコープが破棄された後も、そのブロックを使用する可能性がある場合にのみ、ブロックのコピーを作成する必要があります。ブロックをコピーすると、ブロックはヒープに移動します。

ブロックプログラミングトピック.pdf (P.21)


MyAlertView を使用するとこうなる。
delegate は設定する必要がなくなったので nil としたが、もちろん必要なら設定する。
@implementation MyClass
- (void)showAlert
{
    MyAlertView *alertView = [[MyAlertView alloc] initWithTitle:@"Title"
                                                        message:@"Message"
                                                       delegate:nil
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:@"Other", nil];
    [alertView showWithCompletionHandler:^(NSInteger buttonIndex) {
        // Do something.
    }];
    [alertView autorelease];
}
@end


参考リンク
Blocks Programming Topic (iOS Developer Library)
ブロックプログラミングトピック.pdf (iOS Developer Library)