¿Cómo puedo detectar si un teclado externo está presente en un iPad?

¿Hay alguna forma de detectar si un teclado externo (bluetooth o usb) está conectado al iPad?

Una forma indirecta y segura para SDK es hacer que un campo de texto sea el primero en responder. Si el teclado externo está presente, la notificación local UIKeyboardWillShowNotification no se publicará.

Actualización: Esto ya no es cierto desde iOS 9, sin embargo, puede usar las dimensiones del teclado para determinar si se trata de un hardware o teclado de software. Consulte Cómo detectar confiablemente si un teclado externo está conectado en iOS 9? para detalles.

Puede escuchar la notificación de Darwin "GSEventHardwareKeyboardAttached" ( kGSEventHardwareKeyboardAvailabilityChangedNotification ), pero esta es una API privada, por lo que es posible que su aplicación sea rechazada si la usa. Para verificar si el hardware externo está presente, use la función privada GSEventIsHardwareKeyboardAttached() .

UIKit escucha esto y establece la propiedad UIKeyboardImpl.isInHardwareKeyboardMode consecuencia, pero nuevamente esta es una API privada.

Hay otro nivel para esto.

  • Si no tiene un inputAccessoryView, no recibirá la notificación como lo señalan las explicaciones anteriores.
  • Sin embargo, si ha configurado un inputAccessoryView para la vista de texto, recibirá una notificación UIKeyboard cuando el kbd externo esté presente; la lógica es que tendrá que animar su vista en la ubicación correcta para que necesite la animación. información contenida en la notificación.

Afortunadamente, hay suficiente información en el evento para averiguar si se presentará el kbd, aunque todavía está un poco involucrado.

Si examinamos el diccionario de notificación vemos esta información:

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

Eso fue en Portrait; si rotamos el dispositivo a PortraitUpsideDown obtenemos:

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

De manera similar, en LandscapeLeft y LandscapeRight obtenemos diferentes ubicaciones de inicio y finalización.

Hmm … ¿qué significan estos números? Puedes ver que el kbd está fuera de pantalla para comenzar, pero se mueve un poco. Para empeorar las cosas, dependiendo de la orientación del dispositivo, las ubicaciones kbd son diferentes.

Sin embargo, tenemos suficiente información para descubrir qué está pasando:

  1. El kbd se mueve desde fuera de pantalla en la parte inferior física del dispositivo a la misma altura que la entrada AccessorView (pero oscurecida por él)
  2. Entonces, en el caso de Portrait se mueve de 1024 a 980 – debemos tener un inputAccessoryView con una altura de 44, que de hecho es el caso.
  3. Por lo tanto, en Vertical si el extremo y + de la altura de la pantalla AccessAccessoryView ==, entonces el kbd no está visible. Necesitas manejar las otras rotaciones, pero esa es la idea.

Basado en @ user721239 la condición if determina si la parte inferior del teclado está fuera del marco de self.view. “convertRect” normaliza el marco para cualquier orientación.

 - (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); 

Incluso utilizando un inputAccessoryView en su instancia de UITextView configurado como una instancia de UIView con el marco CGRectZero funciona para obtener la entrega de notificaciones del teclado que funcionan con un teclado de hardware.

Este es el código que uso para obtener la altura desde el teclado userInfo en UIKeyboardWillShowNotification . Funciona si con teclados físicos y virtuales.

 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; 

Basado en este hilo, he ensamblado dos métodos estáticos que puedo llamar fácilmente desde los métodos de notificación del teclado para manejar el cambio de tamaño de las vistas (generalmente UIScrollViews) cuando aparece un teclado, independientemente del tipo (software vs hardware):

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

Puede usar lo siguiente que también calcula la altura de la altura del teclado / barra de herramientas cuando se conecta el teclado de hardware. Tendrá que suscribirse a la notificación de KeyboardWillShow:

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

luego maneje la notificación de esta manera:

 - (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 // .... } 

También puede manejar la notificación KeyboardWillHide. Esto se disparará cuando aparezca el primer Respondedor tanto para el hardware como para el teclado del software.

 - (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 // ... } 

Además, no te olvides de eliminar el observador:

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

Como la mayoría de los métodos en las respuestas anteriores han sido obsoletos con iOS 8 y 9, intersecto el marco reportado del teclado con la ventana actual para obtener el marco real del teclado visible. Luego puedes verificar si la altura ha cambiado.

 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! } 

El siguiente código le proporciona el marco del teclado para todas las orientaciones, ya sea que esté utilizando una vista de pantalla completa o la vista de detalle de una vista dividida.

 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; 

El marco del teclado entregado por la notificación siempre está en términos de coordenadas de hardware con el origen como la esquina superior derecha de la pantalla cuando el dispositivo iOS está en modo retrato normal con el botón de inicio en la parte inferior. El método -convertRect: fromView cambia las coordenadas de las coordenadas de la ventana (= hardware) a las coordenadas de la vista local.

Descubrí que con un teclado Bluetooth obtienes un UIKeyboardDidShowNotification la primera vez que hay una rotación de pantalla pero ninguna después. Hace que sea más difícil distinguir el teclado acoplado de los teclados desacoplados / divididos y BT.

Esta no es una respuesta directa para detectar si hay un teclado externo presente, pero estoy haciendo esto para detectar la altura real que se necesita para mostrar las vistas relacionadas con el teclado en la parte inferior de la pantalla.

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

La respuesta de @philosophistry funcionó para mí. La solución es menos complicada en 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);