24/7 twenty-four seven

iOS/OS X application programing topics.

UIKeyCommandを使ってバーコードリーダーの入力を受け取る

TL;DR

  • ●バーコードリーダーは外部キーボードとして扱える
  • UITextFieldなどの入力コンポーネントを使って入力を受け取れる
  • UITextFieldなどを使いたくない場合がある
  • UIKeyCommandを使うと入力コンポーネントを使わずに入力を受け取れる

ユビレジでは商品の入力に市販のバーコードリーダーを利用することができます。

一般的なBluetoothのバーコードリーダーはHID(Human Interface Device)とSPP(Serial Port Profile)の両方のプロファイルに対応しています。

HIDとして接続する場合は外部キーボードと同じ扱いになります。 外部キーボードが繋がっているのと同じなので、UITextFieldUITextViewを使って特別なSDKを必要とせずに入力を受け取ることができます。

ただし、このやり方は簡単なのですが、入力を受け取るにはUITextFieldなどの入力コンポーネントがアクティブになっている必要があるので少し不便です。

ユビレジでは商品点数の多い小売の店舗などではバーコードリーダーメインの入力として使っていますが、飲食店などではバーコードリーダーはまったく使いません。 このように利用者によって、使うところはメインの入力で使うし、使わないところはまったく使わないという状況なので、どちらにも使いやすくなっている必要があります。

そのため、バーコードリーダーの入力を受けるためにはテキスト入力エリアをタップする必要がある、というのは常時バーコードリーダーを利用するユーザーは面倒に感じるでしょう。 かといって常にテキスト入力エリアが自動的にアクティブになるようにすると、バーコードリーダーが接続されてない店舗では常にソフトウェアキーボードが表示されてしまいます。

そこで、ユビレジではバーコードリーダーの入力を受け取るのにUIKeyCommandというiOS 7から新しく追加されたAPIを利用しています。

UIKeyCommandは本来は外部キーボードが接続された状態でキーボードショートカットによる操作をサポートするためのAPIです。 任意のキー入力の組み合わせを受け取ることができるので、入力される可能性のあるすべての文字に反応するようにしておくことで、任意のキー入力を受け取る仕組みとして利用することができます。

UIKeyCommandについては前に別の記事でも書きましたのでそちらもご覧ください。

iOSアプリケーションでキーボードショートカットに対応する - 24/7 twenty-four seven

例えば一般的なバーコードリーダーの入力を受け取るには下記のように実装します。

@interface ViewController ()

@property (nonatomic) NSArray *commands;
@property (nonatomic) NSString *barcode;

@end

@implementation ViewController

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (NSArray *)keyCommands {
    if (!self.commands) {
        NSMutableArray *commands = [[NSMutableArray alloc] init];
        NSArray *characterSets = @[[NSCharacterSet characterSetWithRange:NSMakeRange(0x20, 0x7f - 0x20)],
                                   [NSCharacterSet newlineCharacterSet]];
        for (unichar i = 0x00; i < 0x7f; i++) {
            for (NSCharacterSet *characterSet in characterSets) {
                if ([characterSet characterIsMember:i]) {
                    NSString *string = [[NSString alloc] initWithCharacters:&i length:1];
                    UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:string modifierFlags:kNilOptions action:@selector(handleKeyCommand:)];
                    [commands addObject:command];
                    break;
                }
            }
        }
        
        self.commands = commands.copy;
    }
    
    return self.commands;
}

- (void)handleKeyCommand:(UIKeyCommand *)command {
    NSString *key = command.input;
    NSCharacterSet *newlineCharacterSet = [NSCharacterSet newlineCharacterSet];
    if ([key rangeOfCharacterFromSet:newlineCharacterSet].location != NSNotFound) {
        NSLog(@"Barcode: %@", self.barcode);

        self.barcode = nil;
    } else {
        if (self.barcode) {
            self.barcode = [self.barcode stringByAppendingString:key];
        } else {
            self.barcode = key;
        }
    }
}

@end

まず、UIKeyCommandでキー入力を受け取るにはcanBecomeFirstResponderメソッドでYESを返す必要があります。 そしてkeyCommandsプロパティをオーバーライドして、受け取りたいキーコンビネーションのUIKeyCommandインスタンスの配列を返すようにします。

上記の例ではすべてのASCII文字と、改行文字に反応します。

これで接続されたバーコードリーダーでバーコードを読み取るたびにhandleKeyCommand:メソッドが呼ばれます。 handleKeyCommand:メソッドは1文字ずつ連続で呼ばれるので、終わりが来るまで読み取った文字列を結合します。 たいていのバーコードリーダーでは終わりに改行文字を送ってくるため、それをバーコードの終わりと判断して、そこまでを一つのバーコードと扱います。

このコードはバーコードリーダーが接続されてない場合にはまったく影響ありません。

このように、UIKeyCommandを利用すると、バーコードリーダーを利用している場合は特別な操作を必要とせずにバーコードリーダーからの入力を受け取ることができ、利用しない場合は今までどおり使えるという先述の問題をキレイに解決することができます。

マイナーなAPIですが、意外と便利で、おそらくバーコードリーダー以外にも応用できる場面はあるのではないかと思います。