UITextView beginnt unten oder in der Mitte des Textes

75

Ich werde gleich loslegen. Ich habe eine UItextViewAnsicht in meiner Ansicht, dass die Textansicht manchmal in der Mitte des Textes und manchmal am Ende des Textes beginnt, wenn ein Bildlauf durchgeführt werden muss, um den gesamten Text anzuzeigen (wenn in der Textansicht viel Text vorhanden ist).

Geben Sie hier die Bildbeschreibung ein

Die Bearbeitung ist in der Textansicht nicht aktiviert. Ich brauche eine Möglichkeit, um zu erzwingen, dass die Textansicht jedes Mal oben beginnt. Ich habe einige Fragen wie diese gesehen, bei denen andere Leute einen Inhaltsoffset verwendet haben, aber ich weiß nicht wirklich, wie das funktioniert oder ob es hier überhaupt anwendbar wäre.

Danke für Ihre Hilfe.

Alex Wulff
quelle
Verwenden Sie dort UINavigationBar als Header oder die benutzerdefinierte Ansicht?
Mrunal
@Mrunal Ich bin mir nicht sicher, was du meinst, aber wenn du nach der Farbe fragst, ist es eine normale Navigationsleiste aus einem Push-Segue, nur in der Appdelegate eingefärbt.
Alex Wulff
2
Setzen Sie den Text in viewDidLoad? Wenn Sie dispatch_async in der Hauptwarteschlange ausführen, wird das Problem behoben.
Charlie Wu

Antworten:

169

Das hat den Trick für mich getan!

Ziel c:

[self.textView scrollRangeToVisible:NSMakeRange(0, 0)];

Schnell:

self.textView.scrollRangeToVisible(NSMakeRange(0, 0))

Swift 2 (alternative Lösung)

Fügen Sie diese Überschreibungsmethode Ihrem ViewController hinzu

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    textView.setContentOffset(CGPointZero, animated: false)
}

Swift 3 & 4 (Syntax bearbeiten)

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

  textView.contentOffset = .zero
}
David Cruz
quelle
9
Funktioniert super! Ich verstehe immer noch nicht, warum wir das tun müssen. Warum startet es nicht automatisch bei 0,0?
mikemike396
6
Ich denke, da es sich um einen Benutzereingabebereich handelt, befindet sich der Cursor beim Eingeben von Text am Ende des Textes. Wenn Sie den Bildschirm verlassen, möchten Sie weiter tippen, wenn Sie zurückkehren.
user2700551
2
Ich finde, dass es scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NObesser funktioniert, da es ein anfängliches Rendern vermeidet, das durch die implizite Animation verursacht wird.
Devios1
Das funktioniert bei mir definitiv nicht. Die von @zeeple bereitgestellte Antwort funktioniert, aber wenn wir die Antwort verwendet haben, scrollt die Textansicht nach oben, wenn eine Aktion für die Ansicht ausgeführt wird. Würde mich über Hilfe freuen.
Mshrestha
Hallo Manjul, ich sehe nicht das gleiche Verhalten in meiner Implementierung. Ich kann in der Ansicht berühren, etwas wackeln usw. und es wird nicht automatisch nach oben gescrollt.
Zeeple
54

Alle oben genannten Antworten haben bei mir nicht funktioniert. Es stellt sich jedoch heraus, dass das Geheimnis darin besteht, Ihre Lösung innerhalb einer Überschreibung von viewDidLayoutSubviews zu implementieren, wie in:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

  welcomeText.contentOffset = .zero
}

HTH :)

Zeeple
quelle
2
Vielleicht, weil ich Schreibfedern benutze, ist dies die einzige, die für mich funktioniert hat.
Rodrigo Pinto
3
@ RodrigoPedroso Mit Storyboard und dies war das einzige, das auch für mich funktioniert hat
Patrick
1
Auch hier ist dies die einzige Lösung, die für mich funktioniert hat. Meine Textansicht befindet sich für die Aufzeichnung in einer Schreibfeder. Ich weiß nicht, ob das etwas ändert.
Formular
Ich benutze Storyboard. Könnte dies mit der iOS-Version zusammenhängen?
Miguel Ribeiro
Ich habe es ausprobiert viewDidLayoutSubviews()und gesehen, wie die Textansicht beim Laden des ViewControllers herumrollte, obwohl die Animation auf false gesetzt war. Ich beschloß es , indem sie setContentOffsetin viewWillAppearw / Animation auf unwahr gesetzt.
Adrian
14

In Swift 2

Sie können dies verwenden, um die Textansicht von oben zu starten:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    myTextView.setContentOffset(CGPointZero, animated: false)
}

Bestätigt die Arbeit in Xcode 7.2 mit Swift 2

Nick89
quelle
1
Funktionierte perfekt für mich in Xcode 7.3 (7D175) / iOS 9.3
JSmyth
13

Versuchen Sie diesen folgenden Code -

if ( [self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]){
     self.automaticallyAdjustsScrollViewInsets = NO;         
}

Oder Sie können diese Eigenschaft auch über StoryBoard festlegen -

Wählen Sie ViewController und dann den Attributinspektor aus, der jetzt deaktiviert ist Adjust Scroll View Insets.

keshav vishwkarma
quelle
Ich sehe nicht, wo die Einfügungen der Bildlaufansicht im Attributinspektor vorhanden sind.
Zeeple
@zeeple Es handelt sich um eine ViewController-Eigenschaft, die nach Auswahl von ViewController im Storyboard unter dem Titel des ViewController im Attributinspektor angezeigt wird. Bitte überprüfen Sie richtig, ich hoffe, Sie werden es finden.
Keshav Vishwkarma
8

Für Swift> 2.2 hatte ich Probleme mit iOS 8 und iOS 9 mit den oben genannten Methoden, da es keine einzige Antwort gibt, die funktioniert. Deshalb habe ich Folgendes getan, damit es für beide funktioniert.

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

    if #available(iOS 9.0, *) {
        textView.scrollEnabled = false
    }

    self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    if #available(iOS 9.0, *) {
        textView.scrollEnabled = true
    }
}
CodeOverRide
quelle
Beeindruckend. Ich kann nicht glauben, dass das mein Problem war. Was ist der Vorteil, wenn man es so machen muss?
Sami
6

Aktualisieren Sie die translucentEigenschaft Ihrer UINavigationBar auf NO:

self.navigationController.navigationBar.translucent = NO;

Dadurch wird verhindert, dass die Ansicht unter der Navigationsleiste und der Statusleiste eingerahmt wird.

Wenn Sie die Navigationsleiste ein- und ausblenden müssen, verwenden Sie den folgenden Code in Ihrem viewDidLoad

 if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
    self.edgesForExtendedLayout = UIRectEdgeNone;   // iOS 7 specific

Hoffe das hilft.

Mrunal
quelle
1
Auch hier kein Glück ... die Textansicht wird immer noch ganz nach unten gescrollt. Könnte es etwas mit der Eigenschaft automaticAdjustsScrollViewInsets zu tun haben? Da ich die Textansicht mit dieser Option unter der Navigationsleiste platziert habe, verdunkelt die Navigationsleiste die Textansicht nicht. Danke für die Antwort.
Alex Wulff
Versuchen Sie, die Zeile "AdjustsScrollViewInsets" automatisch zu kommentieren, und überprüfen Sie sie.
Mrunal
5

Xcode 7.2 7c68; IOS 9.1

Mein ViewControllerInhalt UITextViewist kompliziert und hat sich während des Projekts stark verändert (die IDE-Version hat sich möglicherweise auch 2 bis 3 Mal geändert).

Ich habe alle oben genannten Lösungen ausprobiert . Wenn Sie auf dasselbe Problem stoßen, seien Sie PATIENT .

Es gibt drei mögliche "Geheimcodes" zu lösen:

  1. textView.scrollEnabled = false //then set text textView.scrollEnabled = true
  2. textView.scrollRangeToVisible(NSMakeRange(0, 0))
  3. textView.setContentOffset(CGPointZero, animated: false)

Und es gibt zwei Stellen, an denen Sie diese Codes einfügen können:

  1. viewDidLoad()
  2. viewDidLayoutSubviews()

Kombinieren Sie sie, Sie erhalten 3 * 2 = 6 Lösungen. Die richtige Kombination hängt davon ab, wie kompliziert Sie ViewControllersind (glauben Sie mir, nachdem Sie nur eine Ansicht oben gelöscht haben textView, muss ich eine neue Kombination finden).

Und ich fand das:

Wenn Sie "Geheimcodes" eingeben viewDidLayoutSubviews(), aber textView.text = someStringseingeben viewDidLoad(), wird der Inhalt textViewmanchmal "wackeln". Platzieren Sie sie also an derselben Stelle.

Letztes Wort: Probieren Sie ALLE Kombinationen aus. So löse ich diesen dummen Fehler mehr als dreimal in zwei Monaten.

Ausgestoßene
quelle
Das Setzen der Textansicht scrollEnabled auf false und dann auf true hat bei mir funktioniert. Und ich musste es nicht in viewDidLoad oder ViewDidLayoutSubviews einfügen. Ich habe die scrollEnabled = false direkt vor dem Festlegen des Textes eingefügt und dann die scrollEnable = true in einen Completion-Handler für eine Animation eingefügt, die die texView erscheinen lässt
Anna Harrison
5

Bei vielen Tests musste ich unten die Funktion viewWillLayoutSubviews () hinzufügen, um sicherzustellen, dass die UITextView von Anfang an angezeigt wird:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()            
    textViewTerms.scrollRangeToVisible(NSMakeRange(0, 0))
}
Kevin
quelle
4

UITextViewScrollen scheint für viele ein Problem zu sein. Aus den Antworten hier (insbesondere dieser ) und der Apple Developer-Dokumentation, die ich mit meinem eigenen Verstand verwende, ist hier eine Lösung, die für mich funktioniert. Sie können den Code an Ihre Bedürfnisse anpassen.

Mein Anwendungsfall lautet wie folgt: Dasselbe UITextViewwird für verschiedene Zwecke verwendet und zeigt unterschiedliche Inhalte unter verschiedenen Umständen an. Ich möchte, dass bei einer Änderung des Inhalts die alte Bildlaufposition wiederhergestellt oder manchmal bis zum Ende gescrollt wird. Ich möchte nicht zu viel Animation, wenn dies erledigt ist. Insbesondere möchte ich nicht, dass die Ansicht animiert wird, da der gesamte Text neu war. Diese Lösung stellt zuerst die alte Bildlaufposition ohne Animation wieder her und scrollt dann auf Wunsch bis zum animierten Ende.

Was Sie tun müssen (oder sollte ich sagen können ), ist UITextView wie folgt zu erweitern:

extension UITextView {
  func setText(text: String, storedOffset: CGPoint, scrollToEnd: Bool) {
    self.text = text
    let delayInSeconds = 0.001
    let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
    dispatch_after(popTime, dispatch_get_main_queue(), {
      self.setContentOffset(storedOffset, animated: false)
      if scrollToEnd && !text.isEmpty {
        let popTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
        dispatch_after(popTime, dispatch_get_main_queue(), {
          self.scrollRangeToVisible(NSMakeRange(text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) - 1, 0))
        })
      }
    })
  }
}

Dadurch wird der Text aktualisiert, dann ein gespeicherter Wert der UITextView.contentOffsetEigenschaft (oder alles, was Sie als Parameter übergeben) verwendet und der Versatz der Ansicht entsprechend festgelegt. Falls gewünscht, wird danach zum Ende des neuen, möglicherweise geänderten Inhalts gescrollt.

Ich bin neu in der iOS-Programmierung und weiß nicht, warum es so gut funktioniert. Wenn jemand Informationen dazu hat, wäre es schön zu wissen. Auch der Ansatz ist möglicherweise nicht perfekt, daher bin ich auch offen für Verbesserungsvorschläge.

Und natürlich danke an NixonsBack für die Antwort hinter dem obigen Link.

Mein erster Beitrag :), Prost!

ipe
quelle
Dies ist genau das, was ich brauchte, um dieses Problem zu beheben. Vielen Dank!
BlueGuy
4

Fügen Sie diese eine Codezeile in ViewDidLoad ein

self.automaticallyAdjustsScrollViewInsets = false
Murat Yasar
quelle
3

Der folgende Code sollte Ihnen den gewünschten Effekt verleihen.

[self.scrollView setContentOffset:CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];

Sie müssen "self.scrollView" durch den Namen Ihrer Bildlaufansicht ersetzen. Sie sollten diesen Code eingeben, nachdem Sie den Text der Bildlaufansicht festgelegt haben.

Gary Riches
quelle
Ich habe versucht, [self.scrollView setContentOffset:CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];an mehreren Stellen zu platzieren: nachdem ich den Code programmgesteuert festgelegt habe, in viewDidLoad und in viewWillAppear, aber ohne lucK. Ich dachte, es hat etwas mit meiner Navigationsleiste zu tun?
Alex Wulff
2

Das hat bei mir funktioniert:

 override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    textView.scrollRectToVisible(CGRect(origin: CGPointZero, size: CGSizeMake(1.0, 1.0)), animated: false)

}
bpolat
quelle
2

Dies funktionierte bei mir mit Xcode 8.3.3:

-(void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.txtQuestion scrollRangeToVisible:NSMakeRange(0, 0)];
}
Dave
quelle
2

Erstellen Sie eine Steckdose für Ihre UITextViewin der ViewController.swiftDatei. In den ViewDidLoadAbschnitt setzen Sie Folgendes:

Schnell:

self.textView.contentOffset.y = 0

Ich habe versucht:

self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
J. Bury
quelle
1

Ich habe die Antwort von zeeple in MonoTouch / Xamarin (C #) übersetzt.

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();
    myForm.SetContentOffset(new CoreGraphics.CGPoint(0,0), animated: false);
}
kcborys
quelle
0

Ich musste hier zwei Antworten implementieren, damit meine Ansicht so funktioniert, wie ich es möchte:

Von Juan David Cruz Serrano:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    textView.setContentOffset(CGPoint.zero, animated: false)
}

Und von Murat Yasar:

automaticallyAdjustsScrollViewInsets = false

Dies ergab eine UITextView, die mit dem Bildlauf ganz oben geladen wird und bei der die Einfügungen nicht geändert werden, sobald der Bildlauf beginnt. Sehr seltsam, dass dies nicht das Standardverhalten ist.

Leon
quelle
0

textViewVerwenden Sie den folgenden Code, um zu erzwingen, dass der Start jedes Mal oben beginnt:

Swift 4.2::

override func viewDidLayoutSubviews() {
    textView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
}

Objective-C::

- (void)viewDidLayoutSubviews {
    [self.yourTextView setContentOffset:CGPointZero animated:NO];
}
Andy Fedoroff
quelle
0

Swift 4.2 & Swift 5

Stellen Sie den Inhaltsversatz sowohl vor als auch nach dem Festlegen des Texts ein. (auf dem Haupt-Thread)

let animation = false //or whatever you want
self.mainTextView.setContentOffset(.zero, animated: animation)
self.mainTextView.attributedText = YOUR_ATTRIBUTED_TEXT
self.mainTextView.setContentOffset(.zero, animated: animation)
Amirca
quelle
-1

In meinem Fall habe ich eine Textansicht in eine benutzerdefinierte Tabellenansichtszelle geladen. Im Folgenden wird beschrieben, wie sichergestellt wird, dass der Text in einer Textansicht am oberen Rand des Textes in meiner Textansicht in meiner benutzerdefinierten Zelle geladen wird.

1.) Setzen Sie im Storyboard die Textansicht ScrollEnabled = false, indem Sie die Schaltfläche deaktivieren.

2.) Sie setzen isScrollEnabled in der Textansicht nach dem Laden der Ansicht auf true. Ich habe meine in einer kleinen Verzögerung wie unten eingestellt:

override func awakeFromNib() {
    super.awakeFromNib()

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

}

Unabhängig davon, ob Sie sich in meiner Situation befinden oder nicht, setzen Sie scrollEnabled auf false und setzen Sie scrollEnabled beim Laden der Ansicht auf true.

kelsheikh
quelle