web-dev-qa-db-ja.com

iOS 9で外部キーボードが接続されているかどうかを確実に検出する方法は?

IOS 9以前では、外部キーボードが接続されているかどうかを判断する最も信頼できる方法は、UIKeyboardWillShowNotificationをリッスンし、 この質問で説明したように、テキストフィールドを最初のレスポンダーにすることでした 。この通知は、仮想キーボードを使用すると起動しますが、外部キーボードを使用すると起動しません。

ただし、この動作はiOS 9で変更されました。新しいキーボードツールバーが表示されるようになったため、外部キーボードが接続されるとUIKeyboardWillShowNotificationも起動します。

キーボードの高さを検出し、表示されているのが小さいツールバーであるか大きい仮想キーボードであるかを判断することは依然として可能です。ただし、この方法は、キーボードの高さがさまざまなベータ版の間で変更されており、時間の経過とともに同じ状態を維持することはできないため、信頼できません。

IOS 9で使用できるより信頼性の高い方法はありますか?

32
Sarah Elan

元の質問に戻った後、動作する解決策を見つけました。

通常の仮想キーボードが表示されるとき、キーボードフレームは画面の寸法内にあるようです。ただし、物理キーボードが接続され、キーボードツールバーが表示されている場合、キーボードフレームは画面外にあります。キーボードフレームが画面外にあるかどうかを確認して、キーボードツールバーが表示されているかどうかを判断できます。

Objective-C

- (void) keyboardWillShow:(NSNotification *)notification {
    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;

    if ((keyboard.Origin.y + keyboard.size.height) > height) {
        self.hasKeyboard = YES;
    }
}

スウィフト

@objc func keyboardWillShow(_ notification: NSNotification) {
    guard let userInfo = notification.userInfo else {return}
    let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let keyboard = self.view.convert(keyboardScreenEndFrame, from: self.view.window)
    let height = self.view.frame.size.height
    if (keyboard.Origin.y + keyboard.size.height) > height {
        self.hasKeyboard = true
    }
}
49
Sarah Elan

このコードは、iOS 8およびiOS 9、inputAccessoryViewをサポートし、iOSの将来のバージョンでの新しい変更に対応し、新しいデバイスをサポートするために、二重に保護された定数を持っています。

#define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on non-retina iPhone in landscape mode

- (bool)isHardwareKeyboardUsed:(NSNotification*)keyboardNotification {
    NSDictionary* info = [keyboardNotification userInfo];
    CGRect keyboardEndFrame;
    [[info valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
    float height = [[UIScreen mainScreen] bounds].size.height - keyboardEndFrame.Origin.y;
    return height < gThresholdForHardwareKeyboardToolbar;
}

ハードウェアキーボードが表示される場合がありますが、使用されないことに注意してください。

5
Dmitry

プライベートAPIソリューション:(プライベートヘッダーファイルを取得する必要があります-RuntimeViewerを使用します)。

AppStoreの制限がないエンタープライズアプリに最適です。

#import "UIKit/UIKeyboardImpl.h"

+ (BOOL)isHardwareKeyboardMode
{
   UIKeyboardImpl *kbi = [UIKeyboardImpl sharedInstance];
   BOOL externalKeyboard = kbi.inHardwareKeyboardMode;
   NSLog(@"Using external keyboard? %@", externalKeyboard?@"YES":@"NO");
   return externalKeyboard;
}
2
Simon Tillson

サラ・エランの答えのバリエーションを使用しています。私は特定の見解で彼女のアプローチに問題を抱えていました。問題の原因を突き止めることはできませんでした。ただし、これがフルサイズのキーボードではなく、お持ちのios9外部キーボードの「元に戻す」バーであるかどうかを判断する別の方法です。

元に戻すバーのサイズを変更するとブレーキがかかるため、おそらく前方互換性はあまりありません。しかし、それは仕事を成し遂げました。もっと良い方法があるはずなので批判を歓迎します...

//... somewhere ...
#define HARDWARE_KEYBOARD_SIZE_IOS9 55 
//

+ (BOOL) isExternalKeyboard:(NSNotification*)keyboardNotification {

  NSDictionary* info = [keyboardNotification userInfo];
  CGRect keyboardEndFrame;
  [[info valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
  CGRect keyboardBeginFrame;
  [[info valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBeginFrame];

  CGFloat diff = keyboardEndFrame.Origin.y - keyboardBeginFrame.Origin.y;
  return fabs(diff) == HARDWARE_KEYBOARD_SIZE_IOS9;
}
2
Mike

ツールバーを無関係にすると、キーボードが表示されなくなります。これを行うには、左と右のグループを空白にします(少なくともiOS 12.4では):

textField.inputAssistantItem.leadingBarButtonGroups = []
textField.inputAssistantItem.trailingBarButtonGroups = []

...そしてそれがここで役立つ場合、観察するための迅速な方法です:

// Watch for a soft keyboard to show up
let observer = NotificationCenter.default.addObserver(forName: UIWindow.keyboardWillShowNotification, object: nil, queue: nil) { notification in
    print("no external keyboard")
}

// Stop observing shortly after, since the keyboard should have shown by now
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    NotificationCenter.default.removeObserver(observer)
}
1
garafajon

外部デバイスが接続されたときに通知をサブスクライブできます。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceConnected:) name:EAAccessoryDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceDisconnected:) name:EAAccessoryDidDisconnectNotification object:nil];
[[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications];

または、接続されているデバイスのリストを取得するだけです。

EAAccessoryManager* accessoryManager = [EAAccessoryManager sharedAccessoryManager];

if (accessoryManager)
{
    NSArray* connectedAccessories = [accessoryManager connectedAccessories];
    NSLog(@"ConnectedAccessories = %@", connectedAccessories);
}