Come faccio a forzare un UITextView a scorrere verso l'alto ogni volta che cambio il testo?


100

OK, ho qualche problema con il file UITextView. Ecco il problema:

Aggiungo del testo a un file UITextView. L'utente quindi fa doppio clic per selezionare qualcosa. Quindi cambio il testo in UITextView(programmaticamente come sopra) e UITextView scorre fino alla fine della pagina dove c'è un cursore.

Tuttavia, NON è qui che l'utente ha cliccato. Scorre SEMPRE fino alla fine del UITextViewindipendentemente da dove l'utente ha cliccato.

Quindi ecco la mia domanda: come faccio a forzare lo UITextViewscorrimento verso l'alto ogni volta che cambio il testo? Ho provato contentOffsete scrollRangeToVisible. Nessuno dei due funziona.

Tutti i suggerimenti sarebbero apprezzati.

Risposte:


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

Questo lo fa per me.

Versione Swift (Swift 4.1 con iOS 11 su Xcode 9.3):

note.setContentOffset(.zero, animated: true)

13
Non aiutare per me.
Dmitry

6
Funziona, ma perché è necessario comunque? La logica di scorrimento di Apple ha bisogno di raffinatezza, c'è troppo da fare per far sì che le cose si comportino come dovrebbero automaticamente nella maggior parte dei casi.
Giovanni Contarino

4
Funziona per me in iOS 9 ma non prima di viewDidAppear().
Murray Sagal

4
Puoi chiamarlo prima di viewDidAppear () chiamandolo in viewDidLayoutSubviews ()
arcady bob

Ho aggiunto la visualizzazione del testo nella cella della tabella, quindi lo sto impostando nella cella per la riga ma non funziona
Chandni

67

Se qualcuno ha questo problema in iOS 8, ho scoperto che solo l'impostazione della UITextView'sproprietà di testo, quindi chiamando scrollRangeToVisiblecon un NSRangecon location:0, length:0, lavorato. La mia visualizzazione del testo non era modificabile e ho provato sia selezionabile che non selezionabile (nessuna impostazione ha influito sul risultato). Ecco un esempio Swift:

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

grazie, nessuno dei precedenti ha funzionato per me. Il tuo campione sì.
user1244109

Funziona, ma otteniamo un'animazione di scorrimento che deve essere disabilitata, preferisco disabilitare lo scorrimento e riabilitare come suggerito da
@miguel

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

Questo sembra non funzionare più. iOS 9 e Xcode 7.3.1: /
wasimsandhu

Swift 3 Stll funziona! :) copia semplicemente questa riga nel tuo metodo UITextFielDelegate. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (location: 0, length: 0))}`
lifewithelliott

46

Ed ecco la mia soluzione ...

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

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

5
Non amo questa soluzione, ma almeno a partire da iOS 9 beta 5, è l'unica cosa che funziona per me. Presumo che la maggior parte delle altre soluzioni presumono che la visualizzazione del testo sia già visibile. Nel mio caso non è sempre vero, quindi devo continuare a scorrere disabilitato fino a quando non appare.
robotspacer

1
Questa è stata l'unica cosa che ha funzionato anche per me. iOS 9, utilizzando una visualizzazione di testo non modificabile.
SeanR

1
Questo funziona per me, iOS 8 visualizzazione di testo non modificabile con stringa attribuita. In view WillAppear - non funziona
Tina Zh

Sono d'accordo con @robotspacer. e questa soluzione è ancora l'unica che funziona per me.
lerp90

Per me, questa soluzione ha funzionato solo con testi brevi (attribuiti). Quando si hanno testi più lunghi, il bug di scorrimento di UITextView continua a rimanere in vita :-) La mia (un po 'brutta) soluzione era usare dispatch_after con un ritardo di 2 secondi per riattivare lo scorrimento. UITextView sembra aver bisogno di un po 'di tempo per fare il layout del testo ...
LaborEtArs

38

Chiamando

scrollRangeToVisible(NSMakeRange(0, 0))

funziona ma chiamalo

override func viewDidAppear(animated: Bool)

4
Confido che ha funzionato solo per me in viewDidAppear.
eric f.

2
Questo è l'unico che funziona per me. Grazie. Ma come disabilitare l'animazione che trovo leggermente fastidiosa?
rockhammer

Anche se funziona, la soluzione di I @Maria è migliore, perché quella non mostra un breve "salto".
Sipke Schoorstra

1
Riprendo il mio ultimo commento; questa è la risposta migliore per me in quanto funziona sia su iOS 10 che su iOS 11, mentre la risposta di @ Maria non funziona bene su iOS 11.
Sipke Schoorstra

@SipkeSchoorstra, vedi la mia risposta qui sotto per farlo funzionare senza salti (usando KVO).
Terry

29

Stavo usando attributeString in HTML con la visualizzazione del testo non modificabile. L'impostazione dell'offset del contenuto non ha funzionato neanche per me. Questo ha funzionato per me: disabilita lo scorrimento abilitato, imposta il testo e quindi abilita di nuovo lo scorrimento

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

Avevo un textView all'interno di un tableViewCell. In questo caso textView è stato fatto scorrere fino a ca. 50%. Questa soluzione è tanto semplice quanto logica. Grazie
Julian F. Weinert

1
@ JulianF.Weinert Questo non sembra funzionare per iOS10. Ho la tua stessa situazione con il textView in un tableViewCell. Non sto usando la capacità di modifica di textView. Voglio solo testo scorrevole. Ma textView viene sempre fatto scorrere fino a circa il 50% come hai detto. Ho anche provato tutti gli altri suggerimenti con l'impostazione di setContentOffset e scrollRangeToVisible in questo post. Nessuno funziona. C'è qualcos'altro che potresti aver fatto per far funzionare questo?
JeffB6688

@ JeffB6688 scusa, no. È davvero molto tempo fa ... Potrebbe essere un'altra stranezza di una "nuova" versione di iOS?
Julian F. Weinert

Ma non perfettamente su iOS 11.2 sfortunatamente. La risposta di Onlu @RyanTCB funziona sia su iOS 10 che su iOS 11.
Sipke Schoorstra

Questo funziona bene per me finora su iOS 9, 10 e 11, ma ho dovuto abilitarlo viewDidAppearcome parte di una serie più ampia di correzioni
MadProgrammer

19

Dal momento che nessuna di queste soluzioni ha funzionato per me e ho perso troppo tempo a mettere insieme le soluzioni, questo alla fine lo ha risolto per me.

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)
    })
}

È davvero stupido che questo non sia un comportamento predefinito per questo controllo.

Spero che questo aiuti qualcun altro.


2
Questo è stato molto utile! Ho dovuto aggiungere anche: self.automaticallyAdjustsScrollViewInsets = NO; alla visualizzazione contenente UITextView per farlo funzionare completamente.
jengelsma

L'ho fatto funzionare solo in SWIFT 3, con un altro calcolo offset self.textView.contentOffset.ybasato su questa risposta: stackoverflow.com/a/25366126/1736679
Efren

Ha funzionato per me una volta che mi sono adattato alla sintassi più recente per Swift 3
hawmack13

totalmente d'accordo quanto sia folle questo? grazie per la soluzione
ajonno

17

Non sono sicuro di aver capito la tua domanda, ma stai semplicemente cercando di scorrere la visualizzazione verso l'alto? Se è così dovresti farlo

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


1
@SamStewart Il collegamento sembra interrotto, porta sempre alla pagina dei miei account (già loggato) puoi fornire maggiori informazioni su quale soluzione offrono lì?
testimone d'onore

@uliwitness Questa è un'informazione super vecchia ora, consiglierei di provare una risorsa più moderna invece di un post di 8 anni. L'API iOS è cambiata radicalmente in questi 8 anni.
Benjamin Sussman

1
Ho provato a trovare informazioni più recenti. Se sai dove posso trovarlo, lo apprezzerei. Sembra ancora essere lo stesso problema :(
uliwitness

13

Per iOS 10 e Swift 3 ho fatto:

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

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

Ha funzionato per me, non sono sicuro che tu abbia il problema con l'ultima versione di Swift e iOS.


Una soluzione perfetta da seguire!
iChirag

Sto riscontrando il problema di scorrimento con un UITextView in un UIView controllato da un UIPageViewController. (Queste pagine mostrano le mie schermate Aiuto / Informazioni.) La tua soluzione funziona per tutte le pagine tranne la prima. Passare il dito su qualsiasi pagina e tornare alla prima pagina fissa la posizione di scorrimento di quella prima pagina. Ho provato ad aggiungere un altro .isScrollEnabled = False nel viewWillAppear ma questo non ha avuto effetto. Sono perplesso sul motivo per cui la prima pagina si comporta in modo diverso rispetto al resto.
Wayne Henderson

[aggiornamento] L'ho appena risolto! L'introduzione di un ritardo di 0,5 secondi prima che i trigger .isScrollEnabled = true risolvano il problema del primo caricamento.
Wayne Henderson

molto bella . . .
Maysam R

Ha funzionato come un fascino. L'unica soluzione che funziona per me. Tu sei l'uomo.
Lazy Ninja

10

Ho una risposta aggiornata che farà apparire correttamente la visualizzazione del testo e senza che l'utente abbia un'animazione di scorrimento.

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

Perché stai facendo nella coda principale, sine viewWillAppear è in esecuzione sulla coda principale?
karthik Prabhu Alagu

1
Troppe lune fa per ricordarmelo, ma è indirizzato qui: stackoverflow.com/a/17490329/4750649
MacInnis

8

Ecco come ha funzionato su iOS 9 Release in modo che textView venga fatto scorrere in alto prima di apparire sullo schermo

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

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

1
Questo ha funzionato. Per inciso, mi ha insegnato qualcosa di molto importante: la "vista" in "viewwillappear" non si riferisce solo a self.view, ma apparentemente a TUTTE le viste. No?
Sjakelien

8

È così che l'ho fatto

Swift 4:

extension UITextView {

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

 }

Questo è l'unico metodo che produce i migliori risultati per me. Voto positivo.
SmoothBlue

7

Questo ha funzionato per me

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

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

Questo è molto strano. Sulla maggior parte dei dispositivi, DispatchQueue.main.async(...non è necessario avvolgere la chiamata , ma per qualche motivo, (ad esempio) su iPhone 5s (iOS 12.1) non funziona a meno che non venga inviata. Ma come riportato dallo stack di chiamate sul debugger, viewWillAppear() è già in esecuzione sulla coda principale in tutti i casi ... - WTF, Apple ???
Nicolas Miari

2
Mi chiedevo anche perché devo inviare esplicitamente sulla coda principale anche se sto eseguendo tutti gli eventi sulla coda principale. Ma questo ha funzionato per me
Ankit garg

6

Prova questo per spostare il cursore all'inizio del testo.

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

Guarda come va. Assicurati di chiamarlo dopo che tutti i tuoi eventi sono stati attivati ​​o che cosa stai facendo sia stato fatto.


provato amico, senza fortuna. Sembra essere qualcosa con il primo risponditore. Qualche altro suggerimento?
Sam Stewart,

ho lo stesso problema con la posizione del cursore, ha funzionato per me ... ottimo +1 per quello.
Dhaval

ho ottenuto l'animazione dal basso verso l'alto ma non l'ho richiesta, quindi come rimuoverla.
Dhaval

2
Non scorre verso l'alto (meno su pochi pixel).
Dmitry

6

Il mio problema è impostare textView per scorrere verso l'alto quando viene visualizzata la vista. Per iOS 10.3.2 e Swift 3.

Soluzione 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)
}

Soluzione 2:

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

5

Combinate le risposte precedenti, ora dovrebbe funzionare:

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

lmao l'unica cosa che ha funzionato per me quanto sciocca. Sono stato in grado di rimuovere scrollRangeToVisibleperò.
Jordan H

Dovrai sostituire NSRange (0,0) con NSMakeRange (0,0)
RobP,

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

Questa riga di codice funziona per me.


5

solo tu puoi usare questo codice

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

4

Risposta rapida 2:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Ciò impedisce al textView di scorrere fino alla fine del testo dopo averlo impostato.


4

In Swift 3:

  • viewWillLayoutSubviews è il luogo per apportare modifiche. Anche viewWillAppear dovrebbe funzionare, ma logicamente, il layout dovrebbe essere eseguito in viewWillLayoutSubviews .

  • Per quanto riguarda i metodi, funzioneranno sia scrollRangeToVisible che setContentOffset . Tuttavia, setContentOffset consente di attivare o disattivare l'animazione.

Supponiamo che UITextView sia denominato yourUITextView . Ecco il codice:

// 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 viene chiamato su qualsiasi ridimensionamento della vista. Generalmente non è quello che vuoi.
testimone dell'ulisse

4

Ho dovuto chiamare il seguente all'interno di viewDidLayoutSubviews (chiamare all'interno di viewDidLoad era troppo presto):

    myTextView.setContentOffset(.zero, animated: true)

non funziona in tutti gli scenari
Raja Saad il

3

Questo ha funzionato per me. Si basa sulla risposta di RyanTCB ma è la variante Objective-C della stessa soluzione:

- (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 viene chiamato su qualsiasi ridimensionamento della vista. Generalmente non è quello che vuoi.
testimone dell'ulisse

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

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappearlo farà immediatamente prima che l'utente possa notarlo, ma la ViewDidDisappearsezione lo animerà pigramente.
  • [mytextView setContentOffset:CGPointZero animated:YES]; A volte NON funziona (non so perché), quindi usa scrollrangetovisible piuttosto.

3

Problema risolto: iOS = 10.2 Swift 3 UITextView

Ho appena usato la seguente riga:

displayText.contentOffset.y = 0

1
Funzionato (iOS 10.3)! L'ho aggiunto all'interno in viewDidLayoutSubviewsmodo che accada quando lo schermo ruota. In caso contrario, l'offset del contenuto diventa disallineato.
Cucciolo

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

Modifica con maggiori dettagli per favore.
Schemetrico

2

Ho avuto il problema, ma nel mio caso la visualizzazione del testo è la sottoview di alcune visualizzazioni personalizzate e l'ho aggiunta a ViewController. Nessuna delle soluzioni ha funzionato per me. Per risolvere il problema aggiunto un ritardo di 0,1 sec e quindi abilitato lo scorrimento. Ha funzionato per me.

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

Per me funziona bene questo codice:

    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

Per scorrere fino alla posizione esatta e mostrarlo nella parte superiore dello schermo, utilizzo questo codice:

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

L'impostazione di contentOffset.y scorre fino alla fine del testo, quindi scrollRangeToVisible scorre fino al valore di scrollToLocation. In tal modo, la posizione necessaria appare nella prima riga di scrollView.


1

Per me in iOS 9 ha smesso di funzionare per me con la stringa attribuita con il metodo scrollRangeToVisible (la riga 1 era con carattere in grassetto, le altre righe erano con carattere normale)

Quindi ho dovuto usare:

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

dove il ritardo è:

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

Prova a utilizzare questa soluzione a 2 righe:

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

Questo ha funzionato per me. Il mio UITextView era all'interno di un UICollectionVewCell. Sospetto che scrollRangeToVisible scorra nel punto sbagliato a meno che il layout non sia calcolato correttamente
John Fowler

1

Ecco i miei codici. Funziona bene.

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()
      }
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.