ios ios7 uitextview ios7.1

iOS 7.1 UITextView aún no se desplaza hacia cursor/caret después de la nueva línea



ios7 ios7.1 (5)

Alguien ha creado una subclase que resuelve todos los problemas relacionados con el desplazamiento en UITextView . La implementación no podría ser más sencilla: cambie UITextView por la subclase PSPDFTextView .

Una publicación al respecto, que muestra lo que está arreglado (con bonitas animaciones GIF), está aquí: petersteinberger.com/blog/2014/fixing-uitextview-on-ios-7

El git está aquí: PSPDFTextView

Desde iOS 7, un UITextView no se desplaza automáticamente al cursor cuando el usuario escribe texto que fluye a una nueva línea. Este problema está bien documentado en SO y en otros lugares. Para mí, el problema sigue presente en iOS 7.1. ¿Qué estoy haciendo mal?

Instalé Xcode 5.1 y apunté a iOS 7.1. Estoy usando Auto Layout.

Así es como posiciono el contenido de la vista de texto sobre el teclado:

- (void)keyboardUp:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; UIEdgeInsets contentInset = self.textView.contentInset; contentInset.bottom = keyboardRect.size.height; self.textView.contentInset = contentInset; }

Lo que he intentado: he intentado muchas de las soluciones publicadas para SO sobre este problema en lo que respecta a iOS 7. Todas las soluciones que he intentado no parecen resistir bien las vistas de texto que muestran una cadena atribuida. En los siguientes tres pasos, describo cómo la respuesta más votada en SO ( https://stackoverflow.com/a/19277383/1239263 ) responde al usuario tocando la tecla de retorno por primera vez.

(1.) La vista de texto se convirtió en el primer respondedor en viewDidLoad . Desplácese hasta la parte inferior de la vista de texto donde se encuentra el cursor.

(2.) Antes de escribir un solo carácter, toque la tecla de retorno en el teclado. El caret desaparece de la vista.

(3.) Sin embargo, volver a tocar la tecla de retorno parece normalizar la situación. (Nota: al eliminar la última nueva línea, sin embargo, el caret desaparece una vez más).


Código de solución mejorado para la clase descendiente UITextView :

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #define is_iOS7 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") #define is_iOS8 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") @implementation MyTextView { BOOL settingText; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self]; } return self; } - (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated { CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end]; rect.size.height += textView.textContainerInset.bottom; [textView scrollRectToVisible:rect animated:animated]; } - (void)handleTextViewDidChangeNotification:(NSNotification *)notification { if (notification.object == self && is_iOS7 && !is_iOS8 && !settingText) { UITextView *textView = self; if ([textView.text hasSuffix:@"/n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } } } - (void)setText:(NSString *)text { settingText = YES; [super setText:text]; settingText = NO; }

Tenga en cuenta que no funciona cuando se presiona la tecla Abajo en el teclado Bluetooth.


Lo resolví obteniendo la posición real del cursor y ajustándome a él, aquí está mi método:

- (void) alignTextView:(UITextView *)textView withAnimation:(BOOL)shouldAnimate { // where the blinky caret is CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start]; CGFloat offscreen = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top); CGPoint offsetP = textView.contentOffset; offsetP.y += offscreen + 3; // 3 px -- margin puts caret 3 px above bottom if (offsetP.y >= 0) { if (shouldAnimate) { [UIView animateWithDuration:0.2 animations:^{ [textView setContentOffset:offsetP]; }]; } else { [textView setContentOffset:offsetP]; } } }

Si solo necesita orientar después de que el usuario presione regresar / ingresar, intente:

- (void) textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"/n"]) { [self alignTextView:textView withAnimation:NO]; } }

Déjame saber si funciona para ti!


No encuentro la fuente original pero funciona en iOS7.1

- (void)textViewDidChangeSelection:(UITextView *)textView { if ([textView.text characterAtIndex:textView.text.length-1] != '' '') { textView.text = [textView.text stringByAppendingString:@" "]; } NSRange range0 = textView.selectedRange; NSRange range = range0; if (range0.location == textView.text.length) { range = NSMakeRange(range0.location - 1, range0.length); } else if (range0.length > 0 && range0.location + range0.length == textView.text.length) { range = NSMakeRange(range0.location, range0.length - 1); } if (!NSEqualRanges(range, range0)) { textView.selectedRange = range; } }


Una solución robusta debería resistir en las siguientes situaciones:

(1.) una vista de texto que muestra una cadena atribuida

(2.) una nueva línea creada tocando la tecla de retorno en el teclado

(3.) una nueva línea creada al escribir texto que se desborda a la siguiente línea

(4.) copiar y pegar texto

(5.) una nueva línea creada tocando la tecla de retorno por primera vez (vea los 3 pasos en el OP)

(6.) rotación del dispositivo

(7.) En algún caso no se me ocurre que ...

Para satisfacer estos requisitos en iOS 7.1, parece que aún es necesario desplazarse manualmente hacia el símbolo de la escena.

Es común ver soluciones que se desplazan manualmente a la vista cuando se llama al método de vista de texto delegado textViewDidChange:. Sin embargo, encontré que esta técnica no satisfacía la situación # 5 anterior. Incluso una llamada a layoutIfNeeded antes de desplazarse a la caret no ayudó. En su lugar, tuve que desplazarme hasta el CATransaction dentro de un bloque de finalización de CATransaction :

// this seems to satisfy all of the requirements listed above–if you are targeting iOS 7.1 - (void)textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"/n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } }

¿Por qué funciona esto? No tengo idea. Tendrás que preguntarle a un ingeniero de Apple.

Para completar, aquí está todo el código relacionado con mi solución:

#import "ViewController.h" @interface ViewController () <UITextViewDelegate> @property (weak, nonatomic) IBOutlet UITextView *textView; // full-screen @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *string = @"All work and no play makes Jack a dull boy./n/nAll work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy."; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Verdana" size:30.0]}]; self.textView.attributedText = attrString; self.textView.delegate = self; self.textView.backgroundColor = [UIColor yellowColor]; [self.textView becomeFirstResponder]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardIsUp:) name:UIKeyboardDidShowNotification object:nil]; } // helper method - (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated { CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end]; rect.size.height += textView.textContainerInset.bottom; [textView scrollRectToVisible:rect animated:animated]; } - (void)keyboardIsUp:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; UIEdgeInsets inset = self.textView.contentInset; inset.bottom = keyboardRect.size.height; self.textView.contentInset = inset; self.textView.scrollIndicatorInsets = inset; [self scrollToCaretInTextView:self.textView animated:YES]; } - (void)textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"/n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } } @end

Si encuentra una situación en la que esto no funciona, hágamelo saber.