ios - gifs - how to get#images on imessage
Vista desde la parte superior del teclado similar a la aplicaciĆ³n iMessage (5)
Actualmente estoy intentando básicamente implementar una copia exacta de la aplicación iMessage de Apple.
Eso significa que necesito un UITextView que esté acoplado en la parte inferior de la pantalla, y que se mueva hacia arriba cuando se convierte en el primerResponder. - Eso es bastante fácil en realidad. Hay mil maneras de hacerlo y dos de las más comunes son, por supuesto, animar la vista hacia arriba o hacia abajo si se recibe una notificación . La otra es hacerlo a través del inputAccessoryView . Lamentablemente algunas de las características que uno tiene, la otra no. Y parecen ser mutuamente excluyentes.
El gran problema es la rotación .
He explorado aproximadamente al menos siete proyectos de github diferentes, todos ellos reimplementando la misma funcionalidad / comportamiento que estoy tratando de lograr, pero literalmente todos ellos fallaron miserablemente.
HPGrowingTextView, por ejemplo, que utilizan las aplicaciones oficiales de Facebook / FacebookMessenger / (y posiblemente WhatsApp), es una gran pieza de código no deseado. Tome su iDevice, abra la aplicación de Facebook, vaya al chat, abra el teclado y gire su dispositivo. Si presta atención, notará que la barra de entrada salta ligeramente y deja un espacio en blanco entre el marco del teclado y el suyo. Luego, eche un vistazo a la implementación de Apple en iMessage cuando se muestre el teclado. Es perfecto.
Aparte de eso, contentOffset y EdgeInset-hacking que utiliza la biblioteca HPGrowingTextView me dan pesadillas.
Así que quise hacerlo yo mismo y empezar de cero.
Ahora mismo tengo una implementación muy elegante, elegante y sin trucos de un creciente UITextView, pero falta una parte.
Rotación elegante.
Cuando simplemente ajuste los marcos a sus respectivas nuevas posiciones en el método willRotateToInterfaceOrientation: duration: todo termina funcionando perfectamente PERO tengo el mismo problema que tiene HPGrowingTextView (vea la aplicación de Facebook). Un poco de espacio entre la vista de entrada y el teclado mientras se lleva a cabo la rotación.
Descubrí que cuando se gira el dispositivo a modo horizontal, el teclado vertical que se muestra actualmente no "se transforma", sino que desaparece (envía la notificación ''willHide'') y vuelve a aparecer una versión horizontal (enviando la notificación ''willShow''). La transición es un desvanecimiento muy sutil y posiblemente un cambio de tamaño.
Reimplanté mi proyecto usando inputAccessoryView para ver qué sucede entonces y me sorprendió gratamente. El inputAccessoryView gira en perfecta sincronización con el teclado. No hay espacio / brecha entre los dos.
Lamentablemente, todavía no se me ha ocurrido una idea sobre cómo tener el dock inputAccessoryView en la parte inferior de la pantalla y NO desaparecer / mover fuera de él junto con el teclado ...
Lo que no quiero son soluciones piratas, como ... "bajar el marco ligeramente en el Sistema de Coordenadas de Interinterorientación y luego volver a moverlo cuando se llamó al didRotateFrom ...".
Sé de otra aplicación que ha logrado implementar tal comportamiento y es el "Kik Messenger".
¿Alguien tiene una idea, un consejo o un enlace que no haya visto todavía sobre ese tema?
¡Gracias un montón!
Nota: Una vez que se resuelva este problema, abriré el proyecto de código fuente para que todos puedan beneficiarse porque casi todas las implementaciones que pude encontrar en el transcurso de los últimos días, son un desastre.
Aquí hay una subclase de UITextView
que funciona correctamente en iOS 9.3.1 y 8.3.1 . Se encarga de crecer y encogerse con los límites, a la vez que mantiene el cursor en el lugar correcto y anima sin problemas.
Poner la vista sobre el teclado es trivial, con muchas soluciones que se pueden encontrar fácilmente, por lo que no está cubierto ...
No pude encontrar ninguna solución hecha que estuviera lista para la producción, así que terminé trabajando en esto desde cero. Tuve que resolver muchos pequeños problemas en el camino.
Los comentarios de código deberían darle una idea de lo que está pasando.
He compartido esto en mi Github , Contribuciones muy apreciadas.
Notas
- No probado para soportar el paisaje.
- No probado en i6 +
Manifestación
(después de que el elemento de altura máxima se desplaza). Se olvidó de arrastrar la demostración, pero esto también funciona como se espera ...)
Subclase
class ruuiDynamicTextView: UITextView {
var dynamicDelegate: ruuiDynamicTextViewDelegate?
var minHeight: CGFloat!
var maxHeight: CGFloat?
private var contentOffsetCenterY: CGFloat!
init(frame: CGRect, offset: CGFloat = 0.0) {
super.init(frame: frame, textContainer: nil)
minHeight = frame.size.height
//center first line
let size = self.sizeThatFits(CGSizeMake(self.bounds.size.width, CGFloat.max))
contentOffsetCenterY = (-(frame.size.height - size.height * self.zoomScale) / 2.0) + offset
//listen for text changes
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textChanged), name: UITextViewTextDidChangeNotification, object: nil)
//update offsets
layoutSubviews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
//Use content size if more than min size, compensate for Y offset
var height = max(self.contentSize.height - (contentOffsetCenterY * 2.0), minHeight)
var updateContentOffsetY: CGFloat?
//Max Height
if maxHeight != nil && height > maxHeight {
//Cap at maxHeight
height = maxHeight!
} else {
//constrain Y to prevent odd skip and center content to view.
updateContentOffsetY = contentOffsetCenterY
}
//update frame if needed & notify delegate
if self.frame.size.height != height {
self.frame.size.height = height
dynamicDelegate?.dynamicTextViewDidResizeHeight(self, height: height)
}
//constrain Y must be done after setting frame
if updateContentOffsetY != nil {
self.contentOffset.y = updateContentOffsetY!
}
}
func textChanged() {
let caretRect = self.caretRectForPosition(self.selectedTextRange!.start)
let overflow = caretRect.size.height + caretRect.origin.y - (self.contentOffset.y + self.bounds.size.height - self.contentInset.bottom - self.contentInset.top)
if overflow > 0 {
//Fix wrong offset when cursor jumps to next line un explisitly
let seekEndY = self.contentSize.height - self.bounds.size.height
if self.contentOffset.y != seekEndY {
self.contentOffset.y = seekEndY
}
}
}
}
protocol ruuiDynamicTextViewDelegate {
func dynamicTextViewDidResizeHeight(textview: ruuiDynamicTextView, height: CGFloat)
}
Cómo soluciono este problema para mí:
Tengo ChatViewController
y FooterViewController
como UIContainerView
. Además, tengo la salida de FooterViewController
en FooterViewController
. Luego en ChatViewController
tengo:
override func becomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView? {
if let childViewController = childViewControllers.first as? FooterViewController {
childViewController.contentView.removeFromSuperview()
return childViewController.contentView
}
return nil
}
Otra forma es crear la vista programáticamente y regresar como inputAccessoryView.
He tenido éxito en resolver el problema de una manera bastante elegante (creo, ...).
El código se publicará en Github la próxima semana y se vinculará en esta respuesta.
-
Cómo se hace: hice que la rotación funcionara al elegir la forma de entrada inputAccessoryView.
Nomenclatura:
- ''MessageInputView'' es una vista UIV que contiene mi ''GrowingUITextView'' (también contiene un botón "Enviar" y la imagen de fondo).
- ''ChatView'' es la vista que pertenece al ChatViewController que muestra todos los Chatbubbles y tiene mi ''MessageInputView'' acoplado en la parte inferior.
- ''keyboardAccessoryView'' es un tamaño de UIView vacío: CGRect (0,0,0,0).
Necesitaba averiguar cómo hacer que el MessageInputView se quedara en la pantalla cuando se descartó el teclado. Esa fue la parte difícil. Hice esto creando otra vista ( keyboardAccessoryView ) e hice que GrowingUITextView lo usara como inputAccessoryView. Retuve el keyboardAccessoryView porque necesitaría la referencia más adelante.
Luego recordé algunas de las cosas que hice en mi otro intento (animando los marcos de MessageInputView alrededor de la pantalla cada vez que llegaba una notificación del teclado).
Agregué mi MessageInputView como una subvista a mi ChatView (en la parte inferior). Cada vez que se activa y se llama a los métodos willShow: mediante una notificación de teclado, animo manualmente el marco de MessageInputView a su posición designada en la parte superior. Cuando finaliza la animación y se ejecuta el bloque de finalización, elimino la subvista de ChatView y la agrego al keyboardAccessoryView . Esto hace que se desencadene otra notificación porque el teclado se vuelve a cargar CADA vez que se cambian los marcos / límites de inputAccessoryView. ¡Tienes que ser consciente de eso y manejarlo apropiadamente!
Cuando el teclado está a punto de cerrarse, convierto el marco de mi MessageInputView al sistema de coordenadas de mi ChatView y lo agrego como una subvista. Por lo tanto, se elimina de mi keyboardAccessoryView . Luego, cambio el tamaño del cuadro de KeyAccessoryView de nuevo a CGRect (0,0,0,0) porque, de lo contrario, UIViewAnimationDuration no coincide. Luego permito que se descarte el teclado y tengo mi MessageInputView que lo sigue desde arriba y finalmente se acopla en la parte inferior de la pantalla.
Sin embargo, esto es bastante trabajo por muy poco beneficio.
-
Cuídate.
PD: Si alguien descubre una manera más fácil de hacerlo (perfectamente), hágamelo saber.
Recientemente tuve el mismo problema y tuve que crear una solución personalizada, ya que no estaba del todo satisfecho con las bibliotecas de terceros disponibles. He dividido esta implementación en su propio proyecto GitHub:
A partir de algunas pruebas simples en simuladores de iOS 6.1 7 y 8, las rotaciones parecen seguir correctamente el teclado. La vista también aumentará con el texto y cambiará de tamaño automáticamente en la rotación.
Puedes usar una función de init
muy básica para crearla con el ancho de la pantalla y la altura predeterminada, por ejemplo:
self.messageComposerView = [[MessageComposerView alloc] init];
self.messageComposerView.delegate = self;
[self.view addSubview:self.messageComposerView];
Hay varios otros inicializadores que también están disponibles para permitirle personalizar el marco, el desplazamiento del teclado y la altura máxima de la vista de texto. ¡Vea el readme para más!
Recientemente, escribí una publicación en el blog sobre este problema exacto que describió y cómo resolverlo de manera breve y elegante mediante el uso de notificaciones con el teclado, pero sin usar inputAccessoryView
. Y aunque esta pregunta es bastante antigua, este tema sigue siendo relevante, así que aquí está el enlace a la publicación: Sincronización de la animación de rotación entre el teclado y la vista adjunta
Si no desea profundizar en la explicación larga que se describe en la publicación del blog, aquí tiene una breve descripción con un ejemplo de código:
El principio básico es usar el mismo método que usan todos: observar las notificaciones del teclado para animar la vista adjunta hacia arriba y hacia abajo. Pero además de eso, tiene que cancelar estas animaciones cuando se activan las notificaciones del teclado como consecuencia del cambio de orientación de la interfaz.
Ejemplo de rotación sin cancelación de animación personalizada en el cambio de orientación de la interfaz:
Ejemplo de rotación con cancelación de animación en el cambio de orientación de la interfaz:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
self.animatingRotation = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
self.animatingRotation = NO;
}
- (void)adjustViewForKeyboardNotification:(NSNotification *)notification {
NSDictionary *notificationInfo = [notification userInfo];
// Get the end frame of the keyboard in screen coordinates.
CGRect finalKeyboardFrame = [[notificationInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// Convert the finalKeyboardFrame to view coordinates to take into account any rotation
// factors applied to the window’s contents as a result of interface orientation changes.
finalKeyboardFrame = [self.view convertRect:finalKeyboardFrame fromView:self.view.window];
// Calculate new position of the commentBar
CGRect commentBarFrame = self.commentBar.frame;
commentBarFrame.origin.y = finalKeyboardFrame.origin.y - commentBarFrame.size.height;
// Update tableView height.
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.size.height = commentBarFrame.origin.y;
if (!self.animatingRotation) {
// Get the animation curve and duration
UIViewAnimationCurve animationCurve = (UIViewAnimationCurve) [[notificationInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSTimeInterval animationDuration = [[notificationInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Animate view size synchronously with the appearance of the keyboard.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
[UIView setAnimationBeginsFromCurrentState:YES];
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
[UIView commitAnimations];
} else {
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
}
}