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.

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)

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

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
    }

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)

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];

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.


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];


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.


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

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

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

 }

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

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.


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
}

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

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.


5

Nur Sie können diesen Code verwenden

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

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.


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

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)

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

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

    [textview setContentOffset:CGPointZero animated:NO];
}

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.

3

Problem gelöst: iOS = 10.2 Swift 3 UITextView

Ich habe gerade die folgende Zeile verwendet:

displayText.contentOffset.y = 0

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];

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
}

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.


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

1

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

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

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()
      }
  }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.