2009-05-14 16 views
18

Tengo que actualizar una pequeña cantidad de texto en una UITextView desplazable. Solo insertaré un personaje donde está el cursor actualmente, y lo haré con solo presionar un botón en mi barra de navegación.Detener salto de UITextView al configurar texto programáticamente

Mi problema es que cada vez que llamo al método setText de la vista de texto, salta a la parte inferior del texto. ¡He intentado usar contentOffset y restablecer selectedRange pero no funciona! Aquí está mi ejemplo:

// Remember offset and selection 
CGPoint contentOffset = [entryTextView contentOffset]; 
NSRange selectedRange = [entryTextView selectedRange]; 
// Update text 
entryTextView.text = entryTextView.text; 
// Try and reset offset and selection 
[entryTextView setContentOffset:contentOffset animated:NO]; 
[entryTextView setSelectedRange: selectedRange]; 

¿Hay alguna forma se puede actualizar el texto sin ningún movimiento de desplazamiento en todo ... como si sólo hubiera escrito algo en el teclado?

Editar:

He intentado usar el textViewDidChange: delegar método pero todavía no está desplazándose hasta la ubicación original.

- (void)textViewDidChange:(UITextView *)textView { 
    if (self.programChanged) { 
     [textView setSelectedRange:self.selectedRange]; 
     [textView setContentOffset:self.contentOffset animated:NO]; 
     self.programChanged = NO; 
    } 
} 

- (void)changeButtonPressed:(id)sender { 
    // Remember position 
    self.programChanged = YES; 
    self.contentOffset = [entryTextView contentOffset]; 
    self.selectedRange = [entryTextView selectedRange]; 
    // Update text 
    entryTextView.text = entryTextView.text; 
} 

Respuesta

1

Tome un vistazo a la UITextViewDelegate, creo que el método textViewDidChangeSelection puede permitirle hacer lo que necesita.

+0

Gracias por tu aportación. ¿Qué debería pasar en este método delegado? ¿Esto ocurre cuando debe producirse el restablecimiento final de contentOffset y selectedRange? –

0

No es tan elegante Solución pero funciona así que le importa:

- (IBAction)changeTextProgrammaticaly{ 
    myTextView.text = @"Some text"; 
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(rewindOffset) userInfo:nil repeats:NO]; 
} 

- (void)rewindOffset{ 
    [myTextView setContentOffset:CGPointMake(0,0) animated: NO]; 
} 
+0

(Esta solución vino después de que todas las otras soluciones sugeridas aquí no funcionen para mí) – Tiger

+0

Esto no merece un voto a la baja. A veces, problemas de desplazamiento como estos deben resolverse en el ciclo de ejecución. Sé que podría haber soluciones más elegantes, pero esto funciona y es inofensivo. – phatmann

14

Si utiliza el iPhone 3.0 o posterior, puede resolver este problema:

textView.scrollEnabled = NO; 

//You should know where the cursor will be(if you update your text by appending/inserting/deleting you can know the selected range) so keep it in a NSRange variable. 

Then update text: 
textView.text = yourText; 

textView.scrollEnabled = YES; 
textView.selectedRange = range;//you keep before 

debería funcionar ahora (sin más saltos)

Saludos Meir Assayag

+2

No funciona en iOS 8. – Dmitry

2

Ninguna de las soluciones sugeridas funcionó para mí. -setContentOffset: animated: se desencadena por -setText: 3 veces con YES animado y un contentOffset del final (menos el margen predeterminado de 8pt de una UITextView). Envolví la -setText: en un guardia:

textView.contentOffsetAnimatedCallsDisabled = YES; 
textView.text = text; 
textView.contentOffsetAnimatedCallsDisabled = NO; 

En una subclase UITextView en -setContentOffset: animada: poner

if (contentOffsetAnimatedCallsDisabled) return; // early return 

entre su otra lógica. No te olvides de la súper llamada. Esto funciona.

Raphael

+0

Gracias por la sugerencia, lo intentaré! –

+1

No es una API privada, usted anula UITextView e implementa el comportamiento usted mismo de acuerdo con mi ejemplo. La respuesta de Meir Assayag no funcionó en mi caso y en iOS 3.2. –

+0

En iOS 7, esto no funciona, ya que el método contentOffset se volverá a llamar en el momento de la visualización. Estoy buscando una solución de trabajo. – phatmann

8

Sobre la sugerencia de Meir, aquí está el código que elimina la selección mediante programación (sí sé que hay un botón de menú de selección que lo hace también, pero yo estoy haciendo algo un poco raro) sin tener que desplazarse el punto de vista de texto .

NSRange selectedRange = textView.selectedRange; 
textView.scrollEnabled = NO; 
// I'm deleting text. Replace this line with whatever insertions/changes you want 
textView.text = [textView.text 
       stringByReplacingCharactersInRange:selectedRange withString:@""]; 
selectedRange.length = 0; 
// If you're inserting text, you might want to increment selectedRange.location to be 
// after the text you inserted 
textView.selectedRange = selectedRange; 
textView.scrollEnabled = YES; 
+0

Esta solución (configuración scrollEnabled DESPUÉS de establecer el rango seleccionado, no antes) solucionó un problema de "texto saliente" que estaba teniendo al establecer la propiedad de atributo de texto de una UITextView! Gracias –

+0

No me ayuda. – Dmitry

0

Encontré una solución que funciona de manera confiable en iOS 6 y 7 (y probablemente en versiones anteriores). En una subclase de UITextView, haga lo siguiente:

@interface MyTextView() 
@property (nonatomic) BOOL needToResetScrollPosition; 
@end 

@implementation MyTextView 

- (void)setText:(NSString *)text 
{ 
    [super setText:text]; 
    self.needToResetScrollPosition = YES; 
} 

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    if (self.needToResetScrollPosition) { 
     self.contentOffset = CGPointMake(0, 0); 
     self.needToResetScrollPosition = NO; 
    } 
} 

Ninguna de las otras respuestas funciona en iOS 7 porque va a ajustar las compensaciones de desplazamiento en el tiempo de visualización.

+0

self.contentOffset siempre es 0,0 para mí (esta subclase no tiene ningún efecto para evitar el desplazamiento en iOS 7). – Michael

1

Pregunta anterior, pero tuve el mismo problema con la aplicación iOS 7. Requiere cambiar el contentOffset un poco después del ciclo de ejecución. Aquí hay una idea rápida.

self.clueString = [self.level clueText]; 
CGPoint point = self.clueText.contentOffset; 
self.clueText.attributedText = self.clueString; 
double delayInSeconds = 0.001; // after the run loop update 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
    [self.clueText setContentOffset:point animated:NO]; 
}); 
2

en IOS 7. Hay costuras para ser un error con sizeThatFits y saltos de línea que tienen en su UITextView la solución que encontré que funciona es lo envuelve mediante la desactivación de desplazamiento. De esta manera:

textView.scrollEnabled = NO; 
CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)]; 
textView.scrollEnabled = YES; 

y weird jumping ha sido reparado.

+0

Gran ayuda. ¡Gracias! – JMarsh

+0

En la configuración de iOS 8 'textView.scrollEnabled = NO' en' textDidChange: 'parece deshabilitar el desplazamiento para siempre. Sin embargo, en este sistema operativo parece ser suficiente simplemente alternar 'scrollEnabled' una vez durante' viewDidLoad', es decir, establecerlo en 'NO' y directamente volver a' SÍ' –

4

Las siguientes dos soluciones no funcionan para mí en iOS 8.0.

textView.scrollEnabled = NO; 
[textView.setText: text]; 
textView.scrollEnabled = YES; 

y

CGPoint offset = textView.contentOffset; 
[textView.setText: text]; 
[textView setContentOffset:offset]; 

I fijó un delegado a la Vista de Texto para supervisar el evento de desplazamiento, y se dio cuenta de que después de mi operación para restablecer el desplazamiento, el desplazamiento se pone a 0 de nuevo. Por lo tanto, en su lugar, utilizo la cola de operaciones principal para asegurarme de que mi operación de restauración se realice después de la opción "restablecer a 0".

Aquí está mi solución que funciona para iOS 8.0.

CGPoint offset = self.textView.contentOffset; 
self.textView.attributedText = replace; 
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{ 
    [self.textView setContentOffset: offset]; 
}]; 
+0

Por lo tanto, esto solucionó mi problema al probarlo en el simulador, pero no soluciono el problema cuando lo ejecuto en mi dispositivo. Esto me lleva a creer que todavía es una condición de carrera. Estoy considerando simplemente subclasificar uitextview y reemplazar el método layoutsubviews para que no se ejecute cuando tengo habilitada una propiedad de bloqueo. – Lobsterman

5

Esta decisión funciona para iOS 8:

let offset = textView.contentOffset 
textView.text = newText 
textView.layoutIfNeeded() 
textView.setContentOffset(offset, animated: false) 

Es necesario llamar exactamente setContentOffset:animated: porque sólo esto, se cancelará la animación. textView.contentOffset = offset no cancelará la animación y no ayudará.

3

Para editar el texto de una UITextView, tiene que actualizar su textStorage campo:

[_textView.textStorage beginEditing]; 

NSRange replace = NSMakeRange(10, 2); //enter your editing range 
[_textView.textStorage replaceCharactersInRange:replace withString:@"ha ha$ "]; 

//if you want to edit the attributes 
NSRange attributeRange = NSMakeRange(10, 5); //enter your editing attribute range 
[_textView.textStorage addAttribute:NSBackgroundColorAttributeName value:[UIColor greenColor] range:attributeRange]; 

[_textView.textStorage endEditing]; 

Buena suerte

1

yo golpean un uno similar, si no igual, un problema en IOS9. Cambiar las características de un texto a, por ejemplo, BOLD causó que la vista desplazara la selección fuera de la vista. Me lo solucionaron esto añadiendo una llamada a scrollRangeToVisible después de la setSelectedRange:

[self setSelectedRange:range]; 
    [self scrollRangeToVisible:range]; 
+0

Funciona perfectamente sin saltos perceptibles y es muy simple. ¡No sé cómo me perdí esto! – siburb

1

Finalmente probar esto, comprobado en IOS 10

let offset = textView.contentOffset 
textView.attributedText = newValue 
OperationQueue.main.addOperation { 
    self.textView.setContentOffset(offset, animated: false) 
} 
Cuestiones relacionadas