Ocultar inputAccessoryView si el keyboard de hardware está conectado

Similar a esta pregunta: iPad: detección de keyboard externo , estoy desarrollando una aplicación para iPad que está utilizando campos de text con una inputAccessoryView personalizada para proporcionar funcionalidad adicional para el keyboard virtual.

Sin embargo, si un keyboard de hardware (por ejemplo, un keyboard bluetooth) está conectado al dispositivo, el keyboard del software no se muestra como se esperaba, pero por alguna razón el inputAccessoryView todavía se puede ver en la parte inferior de la pantalla. Además, esto parece provocar el disparo de UIKeyboardDidShowNotification (y, por lo tanto, mover mi vista hacia arriba para evitar la oclusión por el keyboard que en realidad no está presente) incluso si el keyboard de hardware se utiliza para la input.

Encontré varias soluciones para detectar si un keyboard de hardware está conectado, pero todos ellos verifican el estado después de recibir un UIKeyboardDidShowNotification , en ese punto el inputAccessoryView ya está visible (por ejemplo, ¿Cómo puedo detectar si hay un keyboard externo presente en un iPad? ) .

Estoy buscando una forma de mostrar solo una InputAccessoryView si no hay un keyboard de hardware conectado. Por lo tanto, necesito saber si un keyboard de hardware está conectado antes de que se UIKeyboardDidShowNotification un UIKeyboardDidShowNotification .

Las soluciones aceptadas proporcionadas aquí ¿Cómo puedo detectar si un keyboard externo está presente en un iPad? no son una opción para mí ya que utilizan API privadas que pueden hacer que mi aplicación sea rechazada.

Esto es solo una mejora de la respuesta de @arlomedia. Lo que hice fue observar el showShow y didShow.

The WillShow que utilizo para mover mi vista de text a su position para que se mueva a la misma velocidad que el keyboard.

The didShow que utilizo para verificar el tamaño aparente del keyboard con la técnica mencionada y ocultar / mostrar el accesorio InputView de acuerdo con esto.

Es importante que establezca esa vista para que se oculte de manera pnetworkingeterminada, y que cuando se reciba un evento willHide, se ocultará nuevamente.

 - (void) addKeyboardObserver { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHidden:) name:UIKeyboardWillHideNotification object:nil]; } - (void) removeKeyboardObserver { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (void)keyboardWillShow:(NSNotification*)notification { CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; // If we're on iOS7 or earlier and landscape then the height is in the // width. // if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) { keyboardSize.height = keyboardSize.width; } NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]; CGRect textFieldFrame = self.textField.frame; textFieldFrame.origin.y = ([Util screenHeight] - keyboardSize.height) - textFieldFrame.size.height - [Util scaledHeight:10.0]; // Move the text field into place. // [UIView animateWithDuration:rate.floatValue animations:^{ self.answerTextField.frame = textFieldFrame; }]; keyboardShown = YES; } - (void)keyboardDidShow:(NSNotification*)notification { CGRect keyboardBeginFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGSize keyboardSize = keyboardBeginFrame.size; // If we're on iOS7 or earlier and landscape then the height is in the // width. // if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) { keyboardSize.height = ABS(keyboardBeginFrame.origin.x - keyboardEndFrame.origin.x); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations } else { keyboardSize.height = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations } NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]; [UIView animateWithDuration:rate.floatValue animations:^{ if (keyboardSize.height <= self.accessoryBar.frame.size.height) { self.textField.inputAccessoryView.hidden = YES; self.answerTextField.inputAccessoryView.userInteractionEnabled = NO; } else { self.textField.inputAccessoryView.hidden = NO; self.answerTextField.inputAccessoryView.userInteractionEnabled = YES; } }]; keyboardShown = YES; } - (void)keyboardHidden:(NSNotification*)notification { NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]; // Remove/hide the accessory view so that next time the text field gets focus, if a hardware // keyboard is used, the accessory bar is not shown. // [UIView animateWithDuration:rate.floatValue animations:^{ self.textField.inputAccessoryView.hidden = YES; self.answerTextField.inputAccessoryView.userInteractionEnabled = NO; }]; keyboardShown = NO; } 

NOTA Editado para agregar cambio a userInteractionEnabled de manera que un accesorio ocultoView no coma toques.

IIRC, las vistas no cambian de tamaño cuando aparece el keyboard del software. Cambio el tamaño de mis vistas en un método keyboardDidShow desencadenado por una notificación de UIKeyboardDidShow. Por lo tanto, debería ser suficiente para detectar el hardware y el software del keyboard en ese método y, a continuación, podría omitir el cambio de tamaño de la tabla y ocultar la vista de accesorios de input (o ajustar el tamaño de la tabla para acomodar la vista de accesorios de input, si prefiere dejarlo visible )

Para cambiar el tamaño de las vistas correctamente, ya sea que esté o no presente un keyboard de hardware, adapté el código de esta respuesta :

 - (void)keyboardDidShow:(NSNotification *)aNotification { CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations // now you can resize your views based on keyboardHeight // that will be the height of the inputAccessoryView if a hardware keyboard is present } 

Eso es todo lo que necesita si desea dejar visible el InputAccessoryView. Para ocultar esto también, creo que necesitarás establecer una variable de instancia para que puedas acceder a ella en keyboardDidShow:

 UIView *currentInputAccessoryView; - (void)textFieldDidBeginEditing:(UITextField *)textField { self.currentInputAccessoryView = textField.inputAccessoryView; } - (void)textViewDidBeginEditing:(UITextView *)textView { self.currentInputAccessoryView = textView.inputAccessoryView; } - (void)keyboardDidShow:(NSNotification *)aNotification { CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations if (keyboardHeight == 44) { self.currentInputAccessoryView.hidden = YES; keyboardHeight = 0; } // now you can resize your views based on keyboardHeight // that will be 0 if a hardware keyboard is present } 

Mi última forma de resolver este problema fue simplemente agregar un observador para el UIKeyboardWillShowNotification

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

.. y esconder el inputAccessoryView previamente almacenado en una variable de instancia.

 // Called when the UIKeyboardWillShowNotification is sent. - (void)keyboardWillShow:(NSNotification*)notification { NSLog(@"keyboardWillShow"); // get the frame end user info key CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; // calculate the visible portion of the keyboard on the screen CGFloat height = [[UIScreen mainScreen] bounds].size.height - kbEndFrame.origin.y; // check if there is a input accessorry view (and no keyboard visible, eg hardware keyboard) if (self.activeTextField && height <= self.activeTextField.inputAccessoryView.frame.size.height) { NSLog(@"hardware keyboard"); self.activeTextField.inputAccessoryView.hidden = YES; } else { NSLog(@"software keyboard"); self.activeTextField.inputAccessoryView.hidden = NO; } } 

Resultó que el problema fue causado por mí creando dinámicamente el inputAccessoryView de mi subclass UITextField personalizada en su método getter. Recreado inadvertidamente la vista con cada llamada del getter en lugar de reutilizar una variable de instancia con instilación perezosa. Esto resultó en que todas mis asignaciones a la vista se ignoraron, ya que aparentemente el método getter se llamará varias veces cuando se acceda al campo de text y se muestre el keyboard y, por lo tanto, la vista se guardó después de mis asignaciones. La reutilización de la vista guardándola en una variable de instancia solucionó este problema.