¿Cómo fuerzo a un UITextView a desplazarse hacia la parte superior cada vez que cambio el texto?


99

De acuerdo, tengo un problema con el UITextView. Aquí está el problema:

Agrego algo de texto a un UITextView. Luego, el usuario hace doble clic para seleccionar algo. Luego cambio el texto en UITextView(programáticamente como arriba) y los UITextView desplazamientos hacia la parte inferior de la página donde hay un cursor.

Sin embargo, ahí NO es donde el usuario hizo clic. SIEMPRE se desplaza hasta la parte inferior del, UITextViewindependientemente de dónde haya hecho clic el usuario.

Así que aquí está mi pregunta: ¿Cómo fuerzo el UITextViewdesplazamiento hacia la parte superior cada vez que cambio el texto? Lo he intentado contentOffsety scrollRangeToVisible. Ninguno de los dos funciona.

Cualquier sugerencia sera apreciada.

Respuestas:


148
UITextView*note;
[note setContentOffset:CGPointZero animated:YES];

Esto lo hace por mí.

Versión Swift (Swift 4.1 con iOS 11 en Xcode 9.3):

note.setContentOffset(.zero, animated: true)

13
No me ayudes.
Dmitry

6
Esto funciona, pero ¿por qué es necesario de todos modos? La lógica de desplazamiento de Apple necesita refinamiento, hay demasiados saltos de aro para que las cosas se comporten de la manera que deberían automáticamente en la mayoría de los casos.
John Contarino

4
Funciona para mí en iOS 9 pero no antes viewDidAppear().
Murray Sagal

4
Puede llamar a esto antes que viewDidAppear () llamándolo en viewDidLayoutSubviews ()
arcady bob

He agregado la vista de texto en la celda de la tabla, así que lo estoy configurando en la celda para la fila, pero no funciona
Chandni

67

Si alguien tiene este problema en iOS 8, me encontré con que sólo la creación de la UITextView'spropiedad de texto, a continuación, llamar scrollRangeToVisiblecon un NSRangecon location:0, length:0, trabajé. Mi vista de texto no era editable y probé tanto seleccionable como no seleccionable (ninguna configuración afectó el resultado). Aquí hay un ejemplo de Swift:

myTextView.text = "Text that is long enough to scroll"
myTextView.scrollRangeToVisible(NSRange(location:0, length:0))

gracias, nada de lo anterior funcionó para mí. Tu muestra lo hizo.
user1244109

Esto funciona, pero obtenemos una animación de desplazamiento que debe deshabilitarse, prefiero deshabilitar el desplazamiento y volver a habilitarlo como sugirió
@miguel

Objective-C: [myTextView scrollRangeToVisible: NSMakeRange (0, 0)];
estado

Esto parece que ya no funciona. iOS 9 y Xcode 7.3.1: /
wasimsandhu

¡Swift 3 Stll funciona! :) simplemente copie esta línea en su método UITextFielDelegate. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (ubicación: 0, longitud: 0))}`
lifewithelliott

46

Y aquí está mi solución ...

    override func viewDidLoad() {
        textView.scrollEnabled = false
        textView.text = "your text"
    }

    override func viewDidAppear(animated: Bool) {
        textView.scrollEnabled = true
    }

5
No me encanta esta solución, pero al menos a partir de iOS 9 beta 5, es lo único que funciona para mí. Supongo que la mayoría de las otras soluciones suponen que su vista de texto ya está visible. En mi caso, eso no siempre es cierto, por lo que tengo que mantener el desplazamiento desactivado hasta que aparezca.
robotspacer

1
Esto fue lo único que funcionó para mí también. iOS 9, usando una vista de texto no editable.
SeanR

1
Esto funciona para mí, vista de texto no editable de iOS 8 con cadena atribuida. In viewWillAppear - no funciona
Tina Zh

Estoy de acuerdo con @robotspacer. y esta solución sigue siendo la única que me funciona.
lerp90

Para mí, esta solución solo funcionó con textos cortos (atribuidos). Cuando se tienen textos más largos, el error de desplazamiento de UITextView se mantiene vivo :-) Mi solución (un poco fea) fue utilizar dispatch_after con un retraso de 2 segundos para volver a habilitar el desplazamiento. UITextView parece necesitar una pequeña cantidad de tiempo para hacer su diseño de texto ...
LaborEtArs

38

Vocación

scrollRangeToVisible(NSMakeRange(0, 0))

funciona pero llámalo

override func viewDidAppear(animated: Bool)

4
Confieso que solo funcionó para mí en viewDidAppear.
eric f.

2
Este es el único que me funciona. Gracias. Pero, ¿cómo desactivas la animación que encuentro un poco molesta?
rockhammer

Aunque esto funciona, la solución de I @Maria es mejor, porque esa no muestra un breve "salto".
Sipke Schoorstra

1
Retiro mi último comentario; esta es la mejor respuesta para mí, ya que funciona tanto en iOS 10 como en iOS 11, mientras que la respuesta de @ Maria no funciona bien en iOS 11.
Sipke Schoorstra

@SipkeSchoorstra, vea mi respuesta a continuación para que funcione sin saltos (usando KVO).
Terry

29

Estaba usando attributeString en HTML con la vista de texto no editable. Establecer el desplazamiento de contenido tampoco funcionó para mí. Esto funcionó para mí: deshabilite el desplazamiento habilitado, configure el texto y luego habilite el desplazamiento nuevamente

[yourTextView setScrollEnabled:NO];
yourTextView.text = yourtext;
[yourTextView setScrollEnabled:YES];

Tenía un textView dentro de un tableViewCell. En este caso, el textView se desplazó a aprox. 50%. Esta solución es tan simple como lógica. Gracias
Julian F. Weinert

1
@ JulianF.Weinert Esto no parece funcionar para iOS10. Tengo la misma situación que tú con textView en un tableViewCell. No estoy usando la capacidad de edición de textView. Solo quiero texto desplazable. Pero el textView siempre se desplaza aproximadamente al 50% como dijiste. También probé todas las otras sugerencias para configurar setContentOffset y scrollRangeToVisible en esta publicación. Ninguno funciona. ¿Hay algo más que pueda haber hecho para que esto funcione?
JeffB6688

@ JeffB6688 lo siento, no. Eso fue realmente hace mucho tiempo ... ¿Podría ser otra peculiaridad de un "nuevo" lanzamiento de iOS?
Julian F. Weinert

Pero no perfectamente en iOS 11.2, desafortunadamente. La respuesta de Onlu @RyanTCB funciona tanto en iOS 10 como en iOS 11.
Sipke Schoorstra

Esto funciona bien para mí hasta ahora en iOS 9, 10 y 11, pero tuve que habilitarlo viewDidAppearcomo parte de una serie más amplia de correcciones
MadProgrammer

19

Como ninguna de estas soluciones funcionó para mí y perdí demasiado tiempo juntando soluciones, esto finalmente me resolvió.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        let desiredOffset = CGPoint(x: 0, y: -self.textView.contentInset.top)
        self.textView.setContentOffset(desiredOffset, animated: false)
    })
}

Es realmente una tontería que no sea el comportamiento predeterminado de este control.

Espero que esto ayude a alguien más.


2
¡Esto fue muy útil! También tuve que agregar: self.automaticallyAdjustsScrollViewInsets = NO; a la vista que contiene UITextView para que funcione completamente.
jengelsma

Solo lo hice funcionar en SWIFT 3, con otro cálculo de compensación self.textView.contentOffset.ybasado en esta respuesta: stackoverflow.com/a/25366126/1736679
Efren

Trabajado para mí una vez me he adaptado a la última sintaxis para Swift 3
hawmack13

totalmente de acuerdo en lo loco que es esto? gracias por la solución
ajonno

17

No estoy seguro de haber entendido su pregunta, pero ¿está intentando simplemente desplazar la vista hacia la parte superior? Si es así, deberías hacerlo

[textview scrollRectToVisible:CGRectMake(0,0,1,1) animated:YES];


1
@SamStewart El enlace parece estar roto, siempre lleva a la página de mi cuenta (ya iniciada sesión). ¿Puede proporcionar más información sobre la solución que ofrecen allí?
uliwitness

@uliwitness Esta es información super antigua ahora, recomendaría probar un recurso más moderno en lugar de una publicación de 8 años. La API de iOS ha cambiado drásticamente en esos 8 años.
Benjamin Sussman

1
He intentado encontrar información más reciente. Si sabe dónde puedo encontrarlo, se lo agradecería. Todavía parece ser el mismo problema :(
uliwitness

13

Para iOS 10 y Swift 3 lo hice:

override func viewDidLoad() {
    super.viewDidLoad()
    textview.isScrollEnabled = false
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    textView.isScrollEnabled = true
}

Funcionó para mí, no estoy seguro de si tiene el problema con la última versión de Swift e iOS.


¡Una solución perfecta a seguir!
iChirag

Tengo el problema de desplazamiento con un UITextView en un UIView controlado por un UIPageViewController. (Estas páginas muestran mis pantallas de Ayuda / Acerca de). Su solución funciona para todas las páginas excepto la primera. Deslizar el dedo a cualquier página y volver a la primera página fija la posición de desplazamiento de esa primera página. Intenté agregar otro .isScrollEnabled = False en viewWillAppear pero esto no tuvo ningún efecto. No sé por qué la primera página se comporta de manera diferente al resto.
Wayne Henderson

[actualización] ¡Acabo de resolverlo! La introducción de un retraso de 0,5 segundos antes de que los disparadores .isScrollEnabled = true solucione el problema de la primera carga.
Wayne Henderson

muy agradable . . .
Maysam R

Trabajado como un encanto. La única solución que funciona para mí. Tu eres el hombre.
Lazy Ninja

10

Tengo una respuesta actualizada que hará que la vista de texto aparezca correctamente y sin que el usuario experimente una animación de desplazamiento.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        self.introductionText.scrollRangeToVisible(NSMakeRange(0, 0))
    })

¿Por qué estás haciendo en la cola principal, sine viewWillAppear se está ejecutando en la cola principal?
karthikPrabhu Alagu

1
Hace demasiadas lunas para recordar, pero se aborda aquí: stackoverflow.com/a/17490329/4750649
MacInnis

8

Así es como funcionó en iOS 9 Release, de modo que textView se desplaza en la parte superior antes de aparecer en la pantalla

- (void)viewDidLoad
{
    [super viewDidLoad];
    textView.scrollEnabled = NO;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    textView.scrollEnabled = YES;
}

1
Esto hizo el truco. Por cierto, me enseñó algo muy importante: la 'vista' en 'viewwillappear' no solo se refiere a self.view, sino aparentemente a TODAS las vistas. ¿No?
Sjakelien

8

Asi es como lo hice

Rápido 4:

extension UITextView {

    override open func draw(_ rect: CGRect)
    {
        super.draw(rect)
        setContentOffset(CGPoint.zero, animated: false)
    }

 }

Este es el único método que produce los mejores resultados para mí. Yo voto a favor.
smoothBlue

7

Esto funcionó para mi

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    DispatchQueue.main.async {
      self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
    }
  }

Esto es muy raro. En la mayoría de los dispositivos, DispatchQueue.main.async(...no es necesario completar la llamada , pero por alguna razón, en (por ejemplo) iPhone 5s (iOS 12.1) no funciona a menos que lo envíe. Pero según lo informado por la pila de llamadas en el depurador, viewWillAppear() ya se está ejecutando en la cola principal en todos los casos ... - WTF, Apple ???
Nicolas Miari

2
También me preguntaba por qué tengo que enviar explícitamente en la cola principal a pesar de que estoy ejecutando todos los eventos en la cola principal. Pero esto funcionó para mí
Ankit garg

6

Intente esto para mover el cursor a la parte superior del texto.

NSRange r  = {0,0};
[yourTextView setSelectedRange:r];

Mira cómo va eso. Asegúrese de llamar a esto después de que se hayan activado todos sus eventos o de lo que esté haciendo.


lo intenté hombre, sin suerte. Parece ser algo con el firstResponder. ¿Cualquier otra sugerencia?
Sam Stewart

tengo el mismo problema con la posición del cursor, funcionó para mí ... excelente +1 para eso.
Dhaval

Tengo la animación de abajo hacia arriba, pero no he requerido eso, así que cómo eliminarlo.
Dhaval

2
No se desplaza hacia la parte superior (menos en unos pocos píxeles).
Dmitry

6

Mi problema es configurar textView para que se desplace hacia la parte superior cuando apareció la vista. Para iOS 10.3.2 y Swift 3.

Solución 1:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // It doesn't work. Very strange.
    self.textView.setContentOffset(CGPoint.zero, animated: false)
}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // It works, but with an animation effection.
    self.textView.setContentOffset(CGPoint.zero, animated: false)
}

Solución 2:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // It works.       
    self.textView.contentOffset = CGPoint.zero
}

5

Combinando las respuestas anteriores, ahora debería funcionar:

talePageText.scrollEnabled = false
talePageText.textColor = UIColor.blackColor()
talePageText.font = UIFont(name: "Bradley Hand", size: 24.0)
talePageText.contentOffset = CGPointZero
talePageText.scrollRangeToVisible(NSRange(location:0, length:0))
talePageText.scrollEnabled = true

Soy lo único que funcionó para mí, qué tontería. Sin scrollRangeToVisibleembargo, pude eliminar .
Jordan H

Deberá reemplazar NSRange (0,0) con NSMakeRange (0,0)
RobP

5
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

Esta línea de código me funciona.


5

solo puedes usar este código

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    textView.scrollRangeToVisible(NSMakeRange(0, 0))
}

4

Respuesta rápida 2:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Esto evita que textView se desplace hasta el final del texto después de configurarlo.


4

En Swift 3:

  • viewWillLayoutSubviews es el lugar para realizar cambios. viewWillAppear también debería funcionar, pero lógicamente, el diseño debería realizarse en viewWillLayoutSubviews .

  • En cuanto a los métodos, tanto scrollRangeToVisible como setContentOffset funcionarán. Sin embargo, setContentOffset permite que la animación esté activada o desactivada.

Suponga que UITextView se denomina suUITextView . Aquí está el código:

// Connects to the TextView in the interface builder.
@IBOutlet weak var yourUITextView: UITextView!

/// Overrides the viewWillLayoutSubviews of the super class.
override func viewWillLayoutSubviews() {

    // Ensures the super class is happy.
    super.viewWillLayoutSubviews()

    // Option 1:
    // Scrolls to a range of text, (0,0) in this very case, animated.
    yourUITextView.scrollRangeToVisible(NSRange(location: 0, length: 0))

    // Option 2:
    // Sets the offset directly, optional animation.
    yourUITextView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
}

viewWillLayoutSubviews se llama en cualquier cambio de tamaño de vista. Generalmente eso no es lo que quieres.
uliwitness

4

Tuve que llamar a la siguiente viewDidLayoutSubviews interior (llamar a viewDidLoad interior fue demasiado pronto):

    myTextView.setContentOffset(.zero, animated: true)

no funciona en todos los escenarios
Raja Saad

3

Esto funcionó para mí. Se basa en la respuesta de RyanTCB, pero es la variante Objective-C de la misma solución:

- (void) viewWillAppear:(BOOL)animated {
    // For some reason the text view, which is a scroll view, did scroll to the end of the text which seems to hide the imprint etc at the beginning of the text.
    // On some devices it is not obvious that there is more text when the user scrolls up.
    // Therefore we need to scroll textView to the top.
    [self.textView scrollRangeToVisible:NSMakeRange(0, 0)];
}

3
-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    [textview setContentOffset:CGPointZero animated:NO];
}

viewWillLayoutSubviews se llama en cualquier cambio de tamaño de vista. Generalmente eso no es lo que quieres.
uliwitness

3
-(void) viewDidAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappear lo hará instantáneamente antes de que el usuario se dé cuenta, pero ViewDidDisappear dé sección lo animará perezosamente.
  • [mytextView setContentOffset:CGPointZero animated:YES]; NO funciona a veces (no sé por qué), así que use scrollrangetovisible en su lugar.

3

Problema resuelto: iOS = 10.2 Swift 3 UITextView

Solo usé la siguiente línea:

displayText.contentOffset.y = 0

1
¡Funcionó (iOS 10.3)! Lo agregué adentro viewDidLayoutSubviewspara que suceda cuando la pantalla gira. De lo contrario, el desplazamiento de contenido se desalineará.
Pup

2
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];

Edite con más detalles por favor.
Schemetrical

2

Tuve el problema, pero en mi caso, la vista de texto es la subvista de una vista personalizada y la agregué a ViewController. Ninguna solución funcionó para mí. Para resolver el problema, se agregó un retraso de 0.1 segundos y luego se habilitó el desplazamiento. Funcionó para mí.

textView.isScrollEnabled = false
..
textView.text = // set your text here

let dispatchTime = DispatchTime.now() + 0.1
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
   self.textView.isScrollEnabled = true
}

1

Para mí funciona bien este código:

    textView.attributedText = newText //or textView.text = ...

    //this part of code scrolls to top
    textView.contentOffset.y = -64
    textView.scrollEnabled = false
    textView.layoutIfNeeded() //if don't work, try to delete this line
    textView.scrollEnabled = true

Para desplazarse a la posición exacta y mostrarlo en la parte superior de la pantalla, uso este código:

    var scrollToLocation = 50 //<needed position>
    textView.contentOffset.y = textView.contentSize.height
    textView.scrollRangeToVisible(NSRange.init(location: scrollToLocation, length: 1))

La configuración de contentOffset.y se desplaza hasta el final del texto y luego scrollRangeToVisible se desplaza hasta el valor de scrollToLocation. Por lo tanto, la posición necesaria aparece en la primera línea de scrollView.


1

Para mí en iOS 9 dejó de funcionar para mí con una cadena atribuida con el método scrollRangeToVisible (la fila 1 estaba con fuente en negrita, otras filas estaban con fuente normal)

Entonces tuve que usar:

textViewMain.attributedText = attributedString
textViewMain.scrollRangeToVisible(NSRange(location:0, length:0))
delay(0.0, closure: { 
                self.textViewMain.scrollRectToVisible(CGRectMake(0, 0, 10, 10), animated: false)
            })

donde el retraso es:

func delay(delay:Double, closure:()->Void) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

1

Intente utilizar esta solución de 2 líneas:

view.layoutIfNeeded()
textView.contentOffset = CGPointMake(textView.contentInset.left, textView.contentInset.top)

Esto funcionó para mí. Mi UITextView estaba dentro de una UICollectionVewCell. Sospecho que scrollRangeToVisible se desplaza al lugar equivocado a menos que el diseño se calcule correctamente
John Fowler

1

Aquí están mis códigos. Funciona bien.

class MyViewController: ... 
{
  private offsetY: CGFloat = 0
  @IBOutlet weak var textView: UITextView!
  ...

  override viewWillAppear(...) 
  {
      ...
      offsetY = self.textView.contentOffset.y
      ...
  }
  ...
  func refreshView() {
      let offSetY = textView.contentOffset.y
      self.textView.setContentOffset(CGPoint(x:0, y:0), animated: true)
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
        [unowned self] in
        self.textView.setContentOffset(CGPoint(x:0, y:self.offSetY), 
           animated: true)
        self.textView.setNeedsDisplay()
      }
  }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.