Wie zwinge ich eine UITextView, jedes Mal, wenn ich den Text ändere, nach oben zu scrollen?

99

OK, ich habe ein Problem mit dem UITextView. Hier ist das Problem:

Ich füge einen Text zu a hinzu UITextView. Der Benutzer doppelklickt dann, um etwas auszuwählen. Ich ändere dann den Text im UITextView(programmgesteuert wie oben) und UITextView scrolle zum Ende der Seite, wo sich ein Cursor befindet.

Hier hat der Benutzer jedoch NICHT geklickt. Es wird IMMER nach unten gescrollt, UITextViewunabhängig davon, wo der Benutzer geklickt hat.

Hier ist meine Frage: Wie zwinge ich das UITextView, jedes Mal, wenn ich den Text ändere, nach oben zu scrollen? Ich habe es versucht contentOffsetund scrollRangeToVisible. Weder Arbeit.

Anregungen wäre dankbar.

Sam Stewart
quelle

Antworten:

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

Das macht es für mich.

Swift-Version (Swift 4.1 mit iOS 11 unter Xcode 9.3):

note.setContentOffset(.zero, animated: true)
Wayne Lo
quelle
13
Hilf mir nicht.
Dmitry
6
Das funktioniert, aber warum ist das überhaupt notwendig? Die Bildlauflogik von Apple muss verfeinert werden. Es gibt zu viele Reifensprünge, um die Dinge so zu verhalten, wie sie sich in den meisten Fällen automatisch verhalten sollten.
John Contarino
4
Funktioniert für mich in iOS 9, aber nicht früher als viewDidAppear().
Murray Sagal
4
Sie können dies früher als viewDidAppear () aufrufen, indem Sie es in viewDidLayoutSubviews ()
aufrufen
Ich habe eine Textansicht in der Zelle der Tabelle hinzugefügt, also setze ich diese in die Zelle für die Zeile, aber sie funktioniert nicht
Chandni
67

Wenn jemand dieses Problem in iOS 8 hat, fand ich , dass nur die Einstellung UITextView'sTexteigenschaft, dann ruft scrollRangeToVisiblemit einem NSRangemit location:0, length:0arbeitete. Meine Textansicht konnte nicht bearbeitet werden, und ich habe sowohl auswählbar als auch nicht auswählbar getestet (keine der Einstellungen hat das Ergebnis beeinflusst). Hier ist ein schnelles Beispiel:

myTextView.text = "Text that is long enough to scroll"
myTextView.scrollRangeToVisible(NSRange(location:0, length:0))
nerd2know
quelle
danke, keiner der oben genannten hat bei mir funktioniert. Ihre Probe hat es getan.
user1244109
Dies funktioniert, aber wir erhalten eine Scroll-Animation, die deaktiviert werden muss. Ich bevorzuge das Deaktivieren des Scrollens und das erneute Aktivieren, wie von @miguel vorgeschlagen
Warpzit
Ziel-C: [myTextView scrollRangeToVisible: NSMakeRange (0, 0)];
Stateful
Das scheint nicht mehr zu funktionieren. iOS 9 und Xcode 7.3.1: /
wasimsandhu
Swift 3 Stll funktioniert! :) Kopieren Sie einfach diese Zeile in Ihre UITextFielDelegate-Methode. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (Position: 0, Länge: 0))}`
lifewithelliott
46

Und hier ist meine Lösung ...

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

    override func viewDidAppear(animated: Bool) {
        textView.scrollEnabled = true
    }
Miguel
quelle
5
Ich mag diese Lösung nicht, aber zumindest ab iOS 9 Beta 5 ist es das einzige, was für mich funktioniert. Ich gehe davon aus, dass die meisten anderen Lösungen davon ausgehen, dass Ihre Textansicht bereits sichtbar ist. In meinem Fall ist das nicht immer der Fall, daher muss ich den Bildlauf deaktiviert lassen, bis er angezeigt wird.
Robotspacer
1
Dies war das einzige, was auch für mich funktionierte. iOS 9 mit einer nicht bearbeitbaren Textansicht.
SeanR
1
Dies funktioniert für mich, iOS 8 nicht bearbeitbare Textansicht mit zugeordneter Zeichenfolge. In viewWillAppear - funktioniert nicht
Tina Zh
Ich stimme @robotspacer zu. und diese Lösung ist immer noch die einzige, die für mich funktioniert.
lerp90
Für mich funktionierte diese Lösung nur mit kurzen (zugeschriebenen) Texten. Bei längeren Texten bleibt der UITextView-Bildlauffehler bestehen :-) Meine (etwas hässliche) Lösung bestand darin, dispatch_after mit einer Verzögerung von 2 Sekunden zu verwenden, um das Scrollen wieder zu aktivieren. UITextView scheint eine kleine Zeit zu benötigen, um sein
Textlayout zu erstellen
38

Berufung

scrollRangeToVisible(NSMakeRange(0, 0))

funktioniert aber ruf es an

override func viewDidAppear(animated: Bool)
RyanTCB
quelle
4
Ich bestätige, dass es nur in viewDidAppear für mich funktioniert hat.
Eric f.
2
Dies ist der einzige, der für mich funktioniert. Vielen Dank. Aber wie deaktiviert man die Animation, die ich etwas nervig finde?
Rockhammer
Obwohl dies funktioniert, ist die Lösung von I @Maria besser, da diese keinen kurzen "Sprung" zeigt.
Sipke Schoorstra
1
Ich nehme meinen letzten Kommentar zurück. Dies ist die beste Antwort für mich, da sie sowohl unter iOS 10 als auch unter iOS 11 funktioniert, während die Antwort von @ Maria unter iOS 11 nicht gut funktioniert.
Sipke Schoorstra
@SipkeSchoorstra, siehe meine Antwort unten, damit es ohne Sprünge funktioniert (mit KVO).
Terry
29

Ich habe attributedString in HTML verwendet, wobei die Textansicht nicht bearbeitet werden konnte. Das Einstellen des Inhaltsversatzes hat auch bei mir nicht funktioniert. Dies funktionierte bei mir: Deaktivieren Sie das Scrollen, setzen Sie den Text und aktivieren Sie das Scrollen erneut

[yourTextView setScrollEnabled:NO];
yourTextView.text = yourtext;
[yourTextView setScrollEnabled:YES];
Maria
quelle
Ich hatte eine Textansicht in einer tableViewCell. In diesem Fall wurde die Textansicht auf ca. 50%. Diese Lösung ist so einfach wie logisch. Vielen Dank
Julian F. Weinert
1
@ JulianF.Weinert Dies scheint für iOS10 nicht zu funktionieren. Ich habe die gleiche Situation wie Sie mit der Textansicht in einer tableViewCell. Ich verwende die Bearbeitungsfunktion von textView nicht. Ich möchte nur scrollbaren Text. Die Textansicht wird jedoch immer auf ca. 50% gescrollt, wie Sie sagten. Ich habe auch alle anderen Vorschläge mit dem Setzen von setContentOffset und scrollRangeToVisible in diesem Beitrag ausprobiert. Keine Arbeit. Gibt es noch etwas, das Sie getan haben, um diese Arbeit zu machen?
JeffB6688
@ JeffB6688 Entschuldigung, nein. Das ist wirklich lange her ... Könnte eine weitere Besonderheit einer "neuen" iOS-Version sein?
Julian F. Weinert
Aber leider nicht perfekt unter iOS 11.2. Die Antwort von Onlu @RyanTCB funktioniert sowohl unter iOS 10 als auch unter iOS 11.
Sipke Schoorstra
Dies funktioniert für mich bisher unter iOS 9, 10 und 11 viewDidAppear
einwandfrei
19

Da keine dieser Lösungen für mich funktionierte und ich viel zu viel Zeit damit verschwendete, Lösungen zusammenzusetzen, löste dies schließlich für mich.

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

Das ist wirklich albern, dass dies kein Standardverhalten für dieses Steuerelement ist.

Ich hoffe das hilft jemand anderem.

Drew S.
quelle
2
Das war sehr hilfreich! Ich musste auch hinzufügen: self.automaticallyAdjustsScrollViewInsets = NO; in die Ansicht mit der UITextView, damit sie vollständig funktioniert.
Jengelsma
Ich habe es nur in SWIFT 3 zum Laufen gebracht , mit einer anderen Offset-Berechnung self.textView.contentOffset.ybasierend auf dieser Antwort: stackoverflow.com/a/25366126/1736679
Efren
Arbeitete für mich, als ich mich an die neueste Syntax für Swift 3
anpasste
stimme vollkommen zu, wie verrückt das ist? Danke für die Lösung
Ajonno
17

Ich bin mir nicht sicher, ob ich Ihre Frage verstehe, aber versuchen Sie einfach, die Ansicht nach oben zu scrollen? Wenn ja, sollten Sie tun

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

Benjamin Sussman
quelle
1
@SamStewart Der Link scheint defekt zu sein und führt immer zu meiner Kontoseite (bereits angemeldet). Können Sie weitere Informationen zu der dort angebotenen Lösung bereitstellen?
uliwitness
@uliwitness Dies sind jetzt super alte Informationen. Ich würde empfehlen, eine modernere Ressource anstelle eines 8 Jahre alten Beitrags auszuprobieren . Die iOS-API hat sich in diesen 8 Jahren dramatisch verändert.
Benjamin Sussman
1
Ich habe versucht, neuere Informationen zu finden. Wenn Sie wissen, wo ich es finden kann, würde ich das schätzen.
Scheint
13

Für iOS 10 und Swift 3 habe ich:

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

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

Arbeitete für mich, nicht sicher, ob Sie das Problem mit der neuesten Version von Swift und iOS haben.

ChallengerGuy
quelle
Eine perfekte Lösung, um zu folgen!
IChirag
Ich habe das Bildlaufproblem mit einer UITextView in einer UIView, die von einem UIPageViewController gesteuert wird. (Diese Seiten zeigen meine Hilfe- / Info-Bildschirme.) Ihre Lösung funktioniert für jede Seite außer der ersten. Durch Wischen zu einer beliebigen Seite und zurück zur ersten Seite wird die Bildlaufposition dieser ersten Seite festgelegt. Ich habe versucht, der viewWillAppear eine weitere .isScrollEnabled = False hinzuzufügen, aber dies hatte keine Auswirkung. Ich bin ratlos darüber, warum sich die erste Seite anders verhält als die anderen.
Wayne Henderson
[Update] Habe es gerade gelöst! Durch die Einführung einer Verzögerung von 0,5 Sekunden vor dem Auslösen von .isScrollEnabled = true wird das Problem beim ersten Laden behoben.
Wayne Henderson
Sehr schön . . .
Maysam R
Lief wie am Schnürchen. Die einzige Lösung, die für mich funktioniert. Du bist der Mann.
Lazy Ninja
10

Ich habe eine aktualisierte Antwort, bei der die Textansicht ordnungsgemäß angezeigt wird und der Benutzer keine Bildlaufanimation erhält.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        self.introductionText.scrollRangeToVisible(NSMakeRange(0, 0))
    })
MacInnis
quelle
Warum wird Sinusansicht in der Hauptwarteschlange in der Hauptwarteschlange ausgeführt?
karthikPrabhu Alagu
1
Vor zu vielen Monden, als dass ich mich erinnern könnte, aber es wird hier angesprochen: stackoverflow.com/a/17490329/4750649
MacInnis
8

So funktionierte es unter iOS 9 Release, sodass die Textansicht oben gescrollt wird, bevor sie auf dem Bildschirm angezeigt wird

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

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    textView.scrollEnabled = YES;
}
ageorgios
quelle
1
Das hat den Trick gemacht. Übrigens hat es mir etwas sehr Wichtiges beigebracht: Die 'Ansicht' in 'Ansicht wird erscheinen' bezieht sich nicht nur auf self.view, sondern anscheinend auf ALLE Ansichten. Nein?
Sjakelien
8

So habe ich es gemacht

Swift 4:

extension UITextView {

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

 }
Maksym Horbenko
quelle
Dies ist die einzige Methode, die für mich die besten Ergebnisse liefert. Ich stimme zu.
SmoothBlue
7

Das hat bei mir funktioniert

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

    DispatchQueue.main.async {
      self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
    }
  }
Ankit garg
quelle
Das ist sehr komisch. Auf den meisten Geräten ist das Einschließen des Anrufs DispatchQueue.main.async(...nicht erforderlich, aber aus irgendeinem Grund funktioniert er auf (z. B.) iPhone 5s (iOS 12.1) nur, wenn Sie ihn versenden. Aber wie vom Aufrufstapel im Debugger gemeldet, viewWillAppear() läuft in allen Fällen bereits in der Hauptwarteschlange ... - WTF, Apple ???
Nicolas Miari
2
Ich habe mich auch gefragt, warum ich explizit in der Hauptwarteschlange versenden muss, obwohl ich alle Ereignisse in der Hauptwarteschlange ausführe. Aber das hat bei mir funktioniert
Ankit garg
6

Versuchen Sie dies, um den Cursor an den Anfang des Textes zu bewegen.

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

Sehen Sie, wie das geht. Stellen Sie sicher, dass Sie dies anrufen, nachdem alle Ihre Ereignisse ausgelöst wurden oder was auch immer Sie tun.

John Ballinger
quelle
versuchte es Mann, kein Glück. Es scheint etwas mit dem FirstResponder zu sein. Irgendwelche anderen Vorschläge?
Sam Stewart
habe das gleiche Problem mit der Cursorposition, es hat bei mir funktioniert ... großartig +1 dafür.
Dhaval
Ich habe Animation von unten nach oben, aber ich habe das nicht benötigt, also wie man das entfernt.
Dhaval
2
Es wird nicht nach oben gescrollt (weniger bei wenigen Pixeln).
Dmitry
6

Mein Problem besteht darin, textView so einzustellen, dass es beim Erscheinen der Ansicht nach oben scrollt. Für iOS 10.3.2 und Swift 3.

Lösung 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)
}

Lösung 2:

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

Kombiniert die vorherigen Antworten, jetzt sollte es funktionieren:

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
Géza Mikló
quelle
lmao das einzige was für mich funktioniert hat wie albern. Ich konnte es jedoch entfernen scrollRangeToVisible.
Jordan H
Sie müssen NSRange (0,0) durch NSMakeRange (0,0) ersetzen
RobP
5
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

Diese Codezeile funktioniert für mich.

Patricia
quelle
5

Nur Sie können diesen Code verwenden

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

Swift 2 Antwort:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Dadurch wird verhindert, dass die Textansicht nach dem Festlegen zum Ende des Texts scrollt.

glasiert
quelle
4

In Swift 3:

  • viewWillLayoutSubviews ist der Ort, an dem Änderungen vorgenommen werden können. viewWillAppear sollte ebenfalls funktionieren, aber logischerweise sollte das Layout in viewWillLayoutSubviews ausgeführt werden .

  • In Bezug auf Methoden funktionieren sowohl scrollRangeToVisible als auch setContentOffset . Mit setContentOffset kann die Animation jedoch ein- oder ausgeschaltet werden.

Angenommen, die UITextView heißt yourUITextView . Hier ist der Code:

// 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)
}
Marco Leong
quelle
viewWillLayoutSubviews wird bei jeder Größenänderung der Ansicht aufgerufen. Das ist im Allgemeinen nicht das, was Sie wollen.
uliwitness
4

Ich musste die folgenden in viewDidLayoutSubviews aufrufen (das Aufrufen in viewDidLoad war zu früh):

    myTextView.setContentOffset(.zero, animated: true)
Charlie Seligman
quelle
funktioniert nicht in jedem Szenario
Raja Saad
3

Das hat bei mir funktioniert. Es basiert auf der Antwort von RyanTCB, ist jedoch die Objective-C-Variante derselben Lösung:

- (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)];
}
Hermann Klecker
quelle
3
-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    [textview setContentOffset:CGPointZero animated:NO];
}
Albert Zou
quelle
viewWillLayoutSubviews wird bei jeder Größenänderung der Ansicht aufgerufen. Das ist im Allgemeinen nicht das, was Sie wollen.
uliwitness
3
-(void) viewDidAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappear wird es sofort tun, bevor der Benutzer es bemerken kann, aber ViewDidDisappear Abschnitt wird es träge animieren.
  • [mytextView setContentOffset:CGPointZero animated:YES]; funktioniert manchmal NICHT (ich weiß nicht warum), also benutze lieber scrollrangetovisible.
Kahng
quelle
3

Problem gelöst: iOS = 10.2 Swift 3 UITextView

Ich habe gerade die folgende Zeile verwendet:

displayText.contentOffset.y = 0
Stephen Rowe
quelle
1
Arbeitete (iOS 10.3)! Ich habe es innen hinzugefügt, viewDidLayoutSubviewsdamit es passiert, wenn sich der Bildschirm dreht. Andernfalls wird der Inhaltsoffset falsch ausgerichtet.
Welpe
2
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];
Shyla
quelle
Bearbeiten Sie bitte mit weiteren Details.
Schema
2

Ich hatte das Problem, aber in meinem Fall ist die Textansicht die Unteransicht einer benutzerdefinierten Ansicht und wurde zu ViewController hinzugefügt. Keine der Lösungen hat bei mir funktioniert. Um das Problem zu lösen, wurde eine Verzögerung von 0,1 Sekunden hinzugefügt und dann das Scrollen aktiviert. Es hat bei mir funktioniert.

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

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

Für mich funktioniert dieser Code gut:

    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

Um zur exakten Position zu scrollen und sie oben auf dem Bildschirm anzuzeigen, verwende ich diesen Code:

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

Wenn Sie contentOffset.y festlegen, wird ein Bildlauf zum Ende des Texts durchgeführt, und scrollRangeToVisible führt einen Bildlauf bis zum Wert von scrollToLocation durch. Dabei erscheint die benötigte Position in der ersten Zeile von scrollView.

Igor
quelle
1

In iOS 9 funktionierte es für mich nicht mehr mit einer zugewiesenen Zeichenfolge mit der Methode scrollRangeToVisible (die erste Zeile war fett gedruckt, die anderen Zeilen waren normal geschrieben).

Also musste ich verwenden:

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

wo Verzögerung ist:

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

Versuchen Sie, diese 2-Zeilen-Lösung zu verwenden:

view.layoutIfNeeded()
textView.contentOffset = CGPointMake(textView.contentInset.left, textView.contentInset.top)
Nikolay Shubenkov
quelle
Das hat bei mir funktioniert. Mein UITextView befand sich in einer UICollectionVewCell. Ich vermute, dass scrollRangeToVisible an die falsche Stelle scrollt, wenn das Layout nicht richtig berechnet wird
John Fowler
1

Hier sind meine Codes. Es funktioniert gut.

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()
      }
  }
David.Chu.ca
quelle