AppleRemoteで自分のアプリを動かしてみる

Coffee & Cocoa » Source CodeここのRemote Control Wrapperを使うとめちゃくちゃ簡単にAppleRemoteでアプリを動かせるようになります。

Remote Control WrapperはMITライセンスでGitHubで公開されてます。
martinkahr/apple_remote_control · GitHub

使い方は極めて簡単です。

まず、デモコードを除くソースコードをプロジェクトに加えます。

  • RemoteControl.[h|m]
  • HIDRemoteControlDevice.[h|m]
  • AppleRemote.[h|m]
  • MultiClickRemoteBehavior.[h|m] (オプション)

以上の8つ。

  • license.txt

はどこかで表示するようにしましょう。

使い方は

  1. 適切なクラスをDelegateとしてAppleRemoteをインスタンス化して保持する。
  2. - (void)remoteButton:(RemoteControlEventIdentifier)identifier pressedDown:(BOOL)pressedDown remoteControl: (RemoteControl*)remoteControlを実装する。

これだけ。
注意してほしいのはそのまま使うとAppleRemoteクラスがdelegateを保持しちゃってるので循環参照が起こってリークすることです。
後で説明するDelegate2段刺しで1段目が勝手に解放されちゃうのを防ぐためだと思われます。
2段刺ししない場合はコードを書き換えちゃいましょう。
通常の使用を想定するとAppleRemoteオブジェクトの生存期間はアプリの起動期間と重なると思うので気付かなかったことにしてもOKです。


で、Delegateの2段刺しとは、MultiClickRemoteBehaviorをAppleRemoteのDelegateとし、実際のハンドリングを行うオブジェクトをMultiClickRemoteBehaviorのDelegateに設定する方法です。
MultiClickRemoteBehaviorはボタンの連続押下を一つのイベントにパッキングしてくれるクラスです。
これを使うと自分で押された回数と前回押された時間を覚えておくことなく、回数によって動作を変更することが可能です。
さらに、ボタンのホールドイベントも生成してくれます。
ボタンの少ないAppleRemoteにとってはありがたいクラスです。


基本的には

MultiClickRemoteBehavior *remoteBehavior = [MultiClickRemoteBehavior new];
[remoteBehavior setDelegate:self];
[remoteBehavior setClickCountingEnabled:YES];

てやれば良いんですが、これだと全部のボタンの回数を数えるようになります。
ボタンの回数を数えるためにボタンの押下からイベント発生までディレイが発生しますので、回数を数えてほしいボタンを指定して他のボタンはすぐにイベントが発生するようにも出来ます。
例えば

MultiClickRemoteBehavior *remoteBehavior = [MultiClickRemoteBehavior new];
[remoteBehavior setDelegate:self];
[remoteBehavior setClickCountEnabledButtons:kRemoteButtonLeft | kRemoteButtonRight];

こんな感じ。

- (void)remoteButton:(RemoteControlEventIdentifier)identifier pressedDown:(BOOL)pressedDown clickCount:(unsigned int)clickCount
はボタンの押したときと離した時の両方のイベントを扱うので処理が長くなりがち。なので、

- (void)remoteButton:(RemoteControlEventIdentifier)identifier pressedDown:(BOOL)pressedDown clickCount:(unsigned int)clickCount
{
    if(pressedDown) {
        [self remoteButtonDown:identifier clickCount:clickCount];
    } else {
        [self remoteButtonUp:identifier clickCount:clickCount];
    }
}

てしちゃえば良いと思う。
実際の処理はファーストレスポンダに丸投げだ!って場合は

- (void)remoteButton:(RemoteControlEventIdentifier)identifier pressedDown:(BOOL)pressedDown clickCount:(unsigned int)clickCount
{
    SEL action = NULL;
    if(pressedDown) {
        action = [self actionForRemoteButtonDown:identifier clickCount:clickCount];
    } else {
        action = [self actionForRemoteButtonUp:identifier clickCount:clickCount];
    }
    if(!action) return;
    
    [NSApp sendAction:action to:nil from:self];
}

とかしちゃう。