ios objective-c ios8 inputaccessoryview

Cambiando el marco de una entradaAccessoryView en iOS 8



objective-c ios8 (9)

Lurker largo tiempo - primer cartel de tiempo!

Estoy teniendo un problema al recrear una barra con un UITextView como lo hace WhatsApp.

Estoy usando una subclase UIView personalizada, y la UIView perezosamente en:

- (UIView *)inputAccessoryView

y devolviendo SÍ a:

- (BOOL)canBecomeFirstResponder

Ahora, quiero cambiar el tamaño de inputAccessoryView cuando el UITextView crezca de tamaño. En iOS 7, simplemente cambiaría el tamaño del marco de dicha vista, y no su origen, y luego llamaría a reloadInputViews y funcionaría: la vista se reloadInputViews hacia arriba para que sea completamente visible por encima del teclado.

En iOS 8, sin embargo, esto no funciona. La única forma de hacerlo funcionar es cambiar el origen del marco a un valor negativo. Esto estaría bien, excepto que crea algunos errores extraños: por ejemplo, UIView vuelve al marco "original" al ingresar cualquier texto.

¿Hay algo que este olvidando? Estoy bastante seguro de que WhatsApp utiliza inputAccessoryView debido a la forma en que descartan el teclado al arrastrar, solo en la última versión de la aplicación.

¡Por favor avísame si puedes ayudarme! O si hay alguna prueba que me gustaría que corriera!

¡Gracias! :)

Por cierto, aquí está el código que estoy usando para actualizar la altura de la vista UIV personalizada llamada composeBar :

// ComposeBar frame size CGRect frame = self.composeBar.frame; frame.size.height += heightDifference; frame.origin.y -= heightDifference; self.composeBar.frame = frame; [self.composeBar.textView reloadInputViews]; // Tried with this [self reloadInputViews]; // and this

Edición: el código fuente completo está disponible en https://github.com/manuelmenzella/SocketChat-iOS


100% funcional y una solución muy simple es enumerar todas las restricciones y establecer un nuevo valor de altura. Aquí hay un código C # (xamarin):

foreach (var constraint in inputAccessoryView.Constraints) { if (constraint.FirstAttribute == NSLayoutAttribute.Height) { constraint.Constant = newHeight; } }


Aquí hay una solución completa e independiente (gracias a @JohnnyC y @ JoãoNunes por señalarme la dirección correcta, @stigi por explaining cómo animar los cambios de contenido intrinsicContent ):

class InputAccessoryView: UIView { // InputAccessoryView is instantiated from nib, but it''s not a requirement override func awakeFromNib() { super.awakeFromNib() autoresizingMask = .FlexibleHeight } override func intrinsicContentSize() -> CGSize { let exactHeight = // calculate exact height of your view here return CGSize(width: UIViewNoIntrinsicMetric, height: exactHeight) } func somethingDidHappen() { // invalidate intrinsic content size, animate superview layout UIView.animateWithDuration(0.2) { self.invalidateIntrinsicContentSize() self.superview?.setNeedsLayout() self.superview?.layoutIfNeeded() } } }


Desafortunadamente, iOS8 agrega una restricción de altura privada a inputAccessoryView, y esta restricción no es pública.

Recomiendo volver a crear la vista de accesorios cuando debería cambiar su marco, y llamar a reloadInputViews para que se instale el nuevo.

Esto es lo que hago, y funciona como se espera.


El problema es que en iOS 8, se instala automáticamente una NSLayoutConstraint que establece la altura de inputAccessoryView igual a su altura de marco inicial. Para solucionar el problema de diseño, debe actualizar esa restricción a la altura deseada y luego instruir a su inputAccessoryView para que se establezca.

- (void)changeInputAccessoryView:(UIView *)inputAccessoryView toHeight:(CGFloat)height { for (NSLayoutConstraint *constraint in [inputAccessoryView constraints]) { if (constraint.firstAttribute == NSLayoutAttributeHeight) { constraint.constant = height; [inputAccessoryView layoutIfNeeded]; break; } } }


He estado golpeando mi cabeza contra la pared en este por bastante tiempo, ya que el comportamiento cambió de iOS 7 a iOS 8. Probé todo, hasta que la solución más obvia de todas me funcionó:

inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

duh


Para resumir la answer de JohnnyC: establezca la máscara de autoresizingMask automático de autoresizingMask a .flexibleHeight , calcule su autoresizingMask de .flexibleHeight intrinsicContentSize y deje que el marco haga el resto.

Código completo, actualizado para Swift 3:

class InputAccessoryView: UIView, UITextViewDelegate { let textView = UITextView() override var intrinsicContentSize: CGSize { // Calculate intrinsicContentSize that will fit all the text let textSize = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: CGFloat.greatestFiniteMagnitude)) return CGSize(width: bounds.width, height: textSize.height) } override init(frame: CGRect) { super.init(frame: frame) // This is required to make the view grow vertically autoresizingMask = .flexibleHeight // Setup textView as needed addSubview(textView) textView.translatesAutoresizingMaskIntoConstraints = false addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[textView]|", options: [], metrics: nil, views: ["textView": textView])) addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[textView]|", options: [], metrics: nil, views: ["textView": textView])) textView.delegate = self // Disabling textView scrolling prevents some undesired effects, // like incorrect contentOffset when adding new line, // and makes the textView behave similar to Apple''s Messages app textView.isScrollEnabled = false } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: UITextViewDelegate func textViewDidChange(_ textView: UITextView) { // Re-calculate intrinsicContentSize when text changes invalidateIntrinsicContentSize() } }


Para solucionar este problema, utilicé inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

Pero, por supuesto, esto provocó que mi vista de texto colapsara. Entonces, agregar una restricción a la barra de herramientas y actualizarla cuando sea necesario, o agregar la restricción a la vista de texto y actualizarla funcionó para mí.


Sí, iOS8 agrega una restricción de altura privada al inputAccessoryView.

Teniendo en cuenta que recrear todo el inputAccessoryView y reemplazar el antiguo puede ser una operación realmente costosa, puede eliminar las restricciones antes de volver a cargar las vistas de entrada

[inputAccessoryView removeConstraints:[inputAccessoryView constraints]]; [textView reloadInputViews];

Solo otra solución


primero, obtenga inputAccessoryView y establezca nil

UIView *inputAccessoryView = yourTextView.inputAccessoryView; yourTextView.inputAccessoryView = nil; [yourTextView reloadInputViews];

a continuación, establecer el marco y el diseño

inputAccessoryView.frame = XXX [inputAccessoryView setNeedsLayout]; [inputAccessoryView layoutIfNeeded];

Último conjunto nuevo inputAccessoryView de nuevo y volver a cargar

yourTextView.inputAccessoryView = inputAccessoryView; [yourTextView reloadInputViews];