web-dev-qa-db-ja.com

iPadに外部キーボードが存在するかどうかを検出するにはどうすればよいですか?

外部(BluetoothまたはUSB)キーボードがiPadに接続されているかどうかを検出する方法はありますか?

51
carloe

間接的でSDKセーフな方法は、テキストフィールドをファーストレスポンダーにすることです。外部キーボードが存在する場合、UIKeyboardWillShowNotificationローカル通知は送信されません。

更新:これはiOS 9以降では事実ではありませんが、キーボードの寸法を使用してハードウェアまたはソフトウェアのキーボードが関係しているかどうかを判断できます。詳細については、iOS 9で外部キーボードが接続されているかどうかを確実に検出する方法を参照してください。

_"GSEventHardwareKeyboardAttached"_(kGSEventHardwareKeyboardAvailabilityChangedNotification)Darwin通知を聞くことができますが、これはプライベートAPIであるため、これを使用するとアプリが拒否される可能性があります。外部ハードウェアが存在するかどうかを確認するには、プライベートGSEventIsHardwareKeyboardAttached()関数を使用します。

UIKitはこれをリッスンし、それに応じて_UIKeyboardImpl.isInHardwareKeyboardMode_プロパティを設定しますが、これもプライベートAPIです。

34
kennytm

これには別のレベルがあります。

  • InputAccessoryViewがない場合、上記の説明が指摘しているように、通知は表示されません。
  • ただし、テキストビューのinputAccessoryViewを設定している場合、外部kbdが存在する場合でもUIKeyboard通知を受け取ります。ロジックは、ビューを正しい場所にアニメーション化する必要があるため、アニメーションが必要です。通知に含まれる情報。

幸いなことに、イベントにはkbdが表示されるかどうかを判断するのに十分な情報がありますが、まだ少し関与しています。

通知辞書を調べると、次の情報が表示されます。

UIKeyboardFrameBeginUserInfoKey = NSRect: {{0, 1024}, {768, 308}}
UIKeyboardFrameEndUserInfoKey = NSRect: {{0, 980}, {768, 308}}

それはポートレートでした。デバイスをPortraitUpsideDownに回転させると、次のようになります。

UIKeyboardFrameBeginUserInfoKey = NSRect: {{0, -308}, {768, 308}}
UIKeyboardFrameEndUserInfoKey = NSRect: {{0, -264}, {768, 308}}

同様に、LandscapeLeftとLandscapeRightでは、開始位置と終了位置が異なります。

うーん...これらの数字はどういう意味ですか? kbdは画面外にあることがわかりますが、少し動きます。さらに悪いことに、デバイスの向きに応じて、kbdの場所が異なります。

ただし、何が起こっているかを把握するのに十分な情報があります。

  1. Kbdは、デバイスの物理的な最下部にあるオフスクリーンからinputAccessoryViewと同じ高さに移動します(ただし、隠されています)。
  2. そのため、Portraitの場合、1024から980に移動します。実際には、高さ44のinputAccessoryViewが必要です。
  3. そのため、Portraitでyの終わりとinputAccessoryViewの高さ==画面の高さの場合、kbdは表示されません。他の回転を処理する必要がありますが、それがアイデアです。
28
user721239

@ user721239に基づいて、if条件により、キーボードの下部がself.viewのフレーム外にあるかどうかが決まります。 「convertRect」は、あらゆる向きのフレームを正規化します。

- (void)keyboardWillShow:(NSNotification *)notification {
keyboardFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil]; // convert orientation
keyboardSize = keyboardFrame.size;
//NSLog(@"keyboardFrame.Origin.y = %f", keyboardFrame.Origin.y);
//NSLog(@"keyboardFrame.size.height = %f", keyboardFrame.size.height);
BOOL hardwareKeyboardPresent = FALSE;;
if ((keyboardFrame.Origin.y + keyboardFrame.size.height) > (self.view.frame.size.height+self.navigationController.navigationBar.frame.size.height)) {
    hardwareKeyboardPresent = TRUE;
}
//NSLog(@"bottomOfKeyboard = %f", bottomOfKeyboard);
//NSLog(@"self.view.frame.size.height = %f", self.view.frame.size.height);
8
T.J.

フレームCGRectZeroでUIViewのインスタンスに設定されたUITextViewインスタンスでinputAccessoryViewを使用しても、ハードウェアキーボードで動作するキーボード通知の配信を取得できます。

5
user1172004

これは、UIKeyboardWillShowNotificationのキーボードuserInfoから高さを取得するために使用するコードです。物理キーボードと仮想キーボードの両方で機能します。

NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

CGRect keyboardRect = [aValue CGRectValue];

CGFloat deviceHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat deviceWidth = [UIScreen mainScreen].bounds.size.width;

CGFloat newKeyboardHeight;

if (interfaceOrientation == UIInterfaceOrientationPortrait)
    newKeyboardHeight = deviceHeight - keyboardRect.Origin.y;
else if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
    newKeyboardHeight = keyboardRect.size.height + keyboardRect.Origin.y;
else if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
    newKeyboardHeight = deviceWidth - keyboardRect.Origin.x;
else
    newKeyboardHeight = keyboardRect.size.width + keyboardRect.Origin.x;
4
philipkd

このスレッドに基づいて、キーボード通知メソッドから簡単に呼び出して、タイプ(ソフトウェアとハ​​ードウェア)に関係なく、キーボードが表示されたときにビュー(通常はUIScrollViews)の適切なサイズ変更を処理できる2つの静的メソッドを組み立てました:

+ (void)keyboardWillShowHide:(NSNotification *)notification
                  inView:(UIView *)view
              adjustView:(UIView *)viewToAdjust
{
    // How much should we adjust the view's frame by?
    CGFloat yOffset = [SMKeyboardUtil keyboardOffsetForKeyboardNotification:notification
                                                                        inView:view];
    CGRect viewFrame = viewToAdjust.frame;
    viewFrame.size.height -= yOffset;

    // Get the animation parameters being used to show the keyboard. We'll use the same animation parameters as we
    // resize our view.
    UIViewAnimationCurve animationCurve;
    NSTimeInterval animationDuration;
    [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];

    // Resize the view's frame to subtract/add the height of the keyboard (and any inputAccessoryView)
    [UIView beginAnimations:@"animate resiz view" context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];
    [viewToAdjust setFrame:viewFrame];
    [UIView commitAnimations];

}

+ (CGFloat)keyboardOffsetForKeyboardNotification:(NSNotification *)notification
                                      inView:(UIView *)view
{
    NSAssert(notification.userInfo[UIKeyboardFrameBeginUserInfoKey], @"Invalid keyboard notification");

    // Get the frame of keyboard from the notification
    CGRect keyboardFrameBeginRaw = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardFrameEndRaw = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

    // Because the frame we get from the notification is raw screen coordinates, without accounting for device orientation,
    // we need to convert the frame to be relative to our view.
    CGRect keyboardFrameBegin = [view convertRect:keyboardFrameBeginRaw fromView:nil];
    CGRect keyboardFrameEnd = [view convertRect:keyboardFrameEndRaw fromView:nil];

    // We could examine the size of the frame, but this does not account for hardware keyboards. Instead,
    // we need to need the delta between the start and end positions to determine how much to modify
    // the size of our view.
    return keyboardFrameBegin.Origin.y - keyboardFrameEnd.Origin.y;
}
3
Mark

以下を使用して、ハードウェアキーボードが接続されている場合のキーボード/ツールバーの高さも計算できます。 KeyboardWillShow通知をサブスクライブする必要があります。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

次に、通知を次のように処理します。

- (void)keyboardWillShow:(NSNotification *)notification
{       
    // Information we want to determine from notification
    BOOL isHardwareKB = NO;
    CGFloat keyboardHeight;

    // Notification info
    NSDictionary* userInfo = [notification userInfo];
    CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect keyboard = [self.view convertRect:keyboardFrame fromView:self.view.window];
    CGFloat height = self.view.frame.size.height;

    // Determine if hardware keyboard fired this notification
    if ((keyboard.Origin.y + keyboard.size.height) > height) {
        isHardwareKB = YES;
        keyboardHeight = height - keyboard.Origin.y;    // toolbar height
    } else {
        isHardwareKB = NO;
        // As this value can change depending on rotation
        keyboardHeight = MIN(keyboardFrame.size.width, keyboardFrame.size.height);
    }

    // adjust view ui constraints ext ext depending on keyboard height 
    //  ....
}

KeyboardWillHide通知を処理することもできます。これは、ハードウェアキーボードとソフトウェアキーボードの両方のfirstResponderで発生します。

- (void)keyboardWillShow:(NSNotification *)notification
{       
    // Information we want to determine from notification
    BOOL isHardwareKB; // this is irrelevant since it is hidden
    CGFloat keyboardHeight = 0; // height is now 0

    // Do any view layout logic here for keyboard height = 0 
    //  ...
}

オブザーバーを削除することも忘れないでください:

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
2
Vlad

前の回答のほとんどのメソッドはiOS 8および9で非推奨になったため、実際に表示されるキーボードフレームを取得するために、キーボードで報告されたフレームを現在のウィンドウと交差させます。次に、高さが変わったかどうかを確認するだけです。

CGRect reportedKeyboardFrameRaw = [[[notification userInfo] valueForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue];

CGRect reportedKeyboardFrame = [self.view.window convertRect: reportedKeyboardFrameRaw fromWindow:nil];

CGRect visibleKeyboardFrame = CGRectIntersection(reportedKeyboardFrame, self.view.window.frame);

if (reportedKeyboardFrame.size.height != visibleKeyboardFrame.size.height)
{
    // External keyboard present!
}
1
Rivera

次のコードは、全画面表示を使用している場合でも、分割表示の詳細表示を使用している場合でも、すべての方向のキーボードフレームを提供します。

NSDictionary* info = [aNotification userInfo];
CGRect frame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [self.view convertRect:frame fromView:nil]; //  The raw frame values are physical device coordinate.
CGSize keyboardSize = keyboardEndFrame.size;

通知によって配信されるキーボードフレームは、iOSデバイスが通常のポートレートモードで、ホームボタンが下部にある場合、Originを画面の右上隅とするハードウェア座標に基づいています。メソッド-convertRect:fromViewは、座標をウィンドウ座標(=ハードウェア)からローカルビュー座標に変更します。

Bluetoothキーボードを使用すると、画面の回転はあるがその後はないという初めてのUIKeyboardDidShowNotificationを取得することがわかりました。ドッキングされたキーボードと、ドッキングされていない/スプリットおよびBTキーボードを区別しにくくします。

0
Paul Linsay

これは、外部キーボードが存在するかどうかを検出するための直接的な答えではありませんが、画面の下部にキーボード関連のビューを表示するために必要な実際の高さを検出するためにこれを行っています。

CGRect keyboardFrame = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
CGFloat keyboardRelatedViewsHeight = self.view.window.frame.size.height - keyboardFrame.Origin.y;
0
user1263865