Как определить, присутствует ли внешняя клавиатура на iPad?

Есть ли способ определить, подключена ли внешняя (bluetooth или USB) клавиатура к iPad?

Ответ 1

Непрямой и безопасный SDK способ состоит в том, чтобы сделать текстовое поле первым ответчиком. Если внешняя клавиатура присутствует, локальное уведомление UIKeyboardWillShowNotification не публикуется.

Вы можете прослушать уведомление "GSEventHardwareKeyboardAttached" (kGSEventHardwareKeyboardAvailabilityChangedNotification) Darwin, но это частный API, так что ваше приложение будет отклонено, если вы его используете. Чтобы проверить наличие внешнего оборудования, используйте закрытую функцию GSEventIsHardwareKeyboardAttached().

UIKit прослушивает это и устанавливает свойство UIKeyboardImpl.isInHardwareKeyboardMode соответствующим образом, но опять же это частный API.

Ответ 2

На этом есть еще один уровень.

  • Если у вас нет inputAccessoryView, вы не получите уведомление, как указано выше.
  • Однако, если вы настроили inputAccessoryView для текстового представления, вы все равно получите уведомление UIKeyboard, когда присутствует внешний kbd, - логика заключается в том, что вам нужно будет оживить ваш вид в нужном месте, чтобы вы нужна информация анимации, содержащаяся в уведомлении.

К счастью, в этом случае достаточно информации, чтобы выяснить, будет ли представлен 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 различны.

Однако у нас достаточно информации, чтобы выяснить, что происходит:

  • kbd перемещается только с экрана на физическом дне устройства до той же высоты, что и inputAccessoryView (но скрывается от него).
  • Итак, в случае портрета он перемещается с 1024 на 980 - у нас должен быть inputAccessoryView с высотой 44, что действительно так.
  • Таким образом, в портрете, если конец y +, высота входного_выбора высоты == высота экрана, то kbd не отображается. Вам нужно обрабатывать другие вращения, но эта идея.

Ответ 3

Основываясь на @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);

Ответ 4

Даже используя inputAccessoryView в экземпляре UITextView, установленном на экземпляр UIView с фреймом, CGRectZero работает, чтобы получить доставку уведомлений клавиатуры, работающих с аппаратной клавиатурой.

Ответ 5

Это код, который я использую для получения высоты с клавиатуры userInfo в UIKeyboardWillShowNotification. Работает как с физической, так и с виртуальной клавиатурой.

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;

Ответ 6

Основываясь на этом потоке, я собрал два статических метода, которые я могу легко вызвать из методов уведомления клавиатуры, чтобы обрабатывать правильное изменение размеров представлений (обычно UIScrollViews), когда появляется клавиатура, независимо от типа (программное обеспечение и аппаратное обеспечение):

+ (void)keyboardWillShowHide:(NSNotification *)notification
                  inView:(UIView *)view
              adjustView:(UIView *)viewToAdjust
{
    // How much should we adjust the view 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 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;
}

Ответ 7

Вы можете использовать следующее, которое также вычисляет высоту для высоты клавиатуры/панели инструментов при подключении аппаратной клавиатуры. Вам нужно будет подписаться на уведомление 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. Это будет огонь, когда первый ответчик будет как для аппаратной, так и для программной клавиатуры.

- (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];
}

Ответ 8

Поскольку большинство методов предыдущих ответов устарели с 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!
}

Ответ 9

Следующий код дает вам рамку клавиатуры для всех ориентаций, используете ли вы полноэкранный режим или подробный вид разделенного вида.

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 в обычном портретном режиме с кнопкой дома внизу. Метод -convertRect: fromView изменяет координаты из оконных координат (= hardware) в локальные координаты вида.

Я обнаружил, что с клавиатурой Bluetooth вы получаете один UIKeyboardDidShowNotification в первый раз, когда есть поворот экрана, но ни один после этого. Делает сложнее отличить пристыкованную клавиатуру от разблокированных/разделенных и BT-клавиатур.

Ответ 10

Это не прямой ответ для обнаружения присутствия внешней клавиатуры, но я делаю это, чтобы определить фактическую высоту, необходимую для отображения связанных с клавиатурой представлений (-ов) в нижней части экрана.

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

Ответ 11

Ответ на @philosophistry работал у меня. Решение на IOS 8 менее сложно:

CGRect keyboardRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

CGFloat deviceHeight = [UIScreen mainScreen].bounds.size.height;    
CGFloat keyboardHeight = deviceHeight - keyboardRect.origin.y;

NSLog(@"actualKeyboardHeight = %f", keyboardHeight);