How do I force a UITextView to scroll to the top every time I change the text?

IphoneIosUitextview

Iphone Problem Overview


OK, I'm having some problem with the UITextView. Here's the issue:

I add some text to a UITextView. The user then double clicks to select something. I then change the text in the UITextView (programatically as above) and the UITextView scrolls to the bottom of the page where there is a cursor.

However, that is NOT where the user clicked. It ALWAYS scrolls to the bottom of the UITextView regardless of where the user clicked.

So here's my question: How do I force the UITextView to scroll to the top every time I change the text? I've tried contentOffset and scrollRangeToVisible. Neither work.

Any suggestions would be appreciated.

Iphone Solutions


Solution 1 - Iphone

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

This does it for me.

Swift version (Swift 4.1 with iOS 11 on Xcode 9.3):

note.setContentOffset(.zero, animated: true)

Solution 2 - Iphone

If anyone has this problem in iOS 8, I found that just setting the UITextView's text property, then calling scrollRangeToVisible with an NSRange with location:0, length:0, worked. My text view was not editable, and I tested both selectable and not selectable (neither setting affected the result). Here's a Swift example:

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

Solution 3 - Iphone

And here is my solution...

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

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

Solution 4 - Iphone

Calling

scrollRangeToVisible(NSMakeRange(0, 0))

works but call it in

override func viewDidAppear(animated: Bool)

Solution 5 - Iphone

I was using attributedString in HTML with text view not editable. Setting the content offset did not work for me either. This worked for me: disable scroll enabled, set the text and then enable the scrolling again

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

Solution 6 - Iphone

Since none of these solutions worked for me and I wasted way too much time piecing together solutions, this finally solved it for 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)
    })
}

This is really silly that this is not default behavior for this control.

I hope this helps someone else out.

Solution 7 - Iphone

I'm not sure if I understand your question, but are you trying to simply scroll the view to the top? If so you should do

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

Solution 8 - Iphone

For iOS 10 and Swift 3 I did:

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

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

Worked for me, not sure if you're having the problem with the latest version of Swift and iOS.

Solution 9 - Iphone

This is how it worked on iOS 9 Release so as the textView is scrolled on top before appearing on screen

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

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

Solution 10 - Iphone

I have an updated answer which will have the textview appear properly, and without the user experiencing a scroll animation.

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

Solution 11 - Iphone

That's how I did it

Swift 4:

extension UITextView {

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

 }

Solution 12 - Iphone

This worked for me

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

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

Solution 13 - Iphone

Try this to move the cursor to the top of the text.

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

See how that goes. Make sure you call this after all your events have fired or what ever you are doing is done.

Solution 14 - Iphone

My issue is to set textView to scroll to top when view appeared. For iOS 10.3.2, and Swift 3.

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

Solution 2:

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

Solution 15 - Iphone

just you can use this code

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

Solution 16 - Iphone

I had to call the following inside viewDidLayoutSubviews (calling inside viewDidLoad was too early):

    myTextView.setContentOffset(.zero, animated: true)

Solution 17 - Iphone

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

This line of code works for me.

Solution 18 - Iphone

Combined the previous answers, now it should work:

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

Solution 19 - Iphone

Swift 2 Answer:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

This prevents the textView from scrolling to the end of the text after setting it.

Solution 20 - Iphone

In Swift 3:

  • viewWillLayoutSubviews is the place to make changes. viewWillAppear should work as well, but logically, layout should be perform in viewWillLayoutSubviews.

  • Regarding methods, both scrollRangeToVisible and setContentOffset will work. However, setContentOffset allows animation to be off or on.

Assume the UITextView is named as yourUITextView. Here's the 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)
}

Solution 21 - Iphone

This worked for me. It is based on RyanTCBs answer but it is the Objective-C variant of the same solution:

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

Solution 22 - Iphone

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    
    [textview setContentOffset:CGPointZero animated:NO];
}

Solution 23 - Iphone

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

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappear will do it instantly before user can notice, but ViewDidDisappear section will animate it lazily.
  • [mytextView setContentOffset:CGPointZero animated:YES]; does NOT work sometimes(I don't know why), so use scrollrangetovisible rather.

Solution 24 - Iphone

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

Solution 25 - Iphone

Problem solved: iOS = 10.2 Swift 3 UITextView

I just used the following line:

displayText.contentOffset.y = 0

Solution 26 - Iphone

I had the problem, but in my case textview is the subview of some custom view and added it to ViewController. None of the solution worked for me. To solve the problem added 0.1 sec delay and then enabled scroll. It worked for 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
}

Solution 27 - Iphone

For me fine works this code:

    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

For scroll to exact position and show it on top of screen I use this code:

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

Setting contentOffset.y scrolls to the end of text, and then scrollRangeToVisible scrolls up to value of scrollToLocation. Thereby, needed position appears in first line of scrollView.

Solution 28 - Iphone

For me in iOS 9 it stopped to work for me with attributed string with method scrollRangeToVisible (the 1 row was with bold font, other rows were with regular font)

So I had to use:

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

where delay is:

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

Solution 29 - Iphone

Try to use this 2 lines solution:

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

Solution 30 - Iphone

Here are my codes. It works fine.

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

Solution 31 - Iphone

Swift 3 version of drew_s answer earlier, which is the only thing that worked for me after trying most of the other answers.

    override func viewWillAppear(_ animated: Bool) {
    DispatchQueue.main.async{
        let desiredOffset = CGPoint(x: 0, y: -self.person_description.contentInset.top)
        self.person_description.setContentOffset(desiredOffset, animated: false)
    }
}

This was a cut and paste of working code. Replace person_description with your textview variable.

Solution 32 - Iphone

I think the reason why some people are having problems with this issue is because they are trying to set the TextView scroll position, before this TextView has appeared.

There is nothing to scroll, when the TextView hasn't yet appeared.

You need to wait for the TextView to appear first, and only then set its scroll position inside the viewDidAppear method as shown below:

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

Solution 33 - Iphone

This code solves the problem and also helps to avoid needless autoscroll to the end of textView. Just use method setTextPreservingContentOffset: instead of setting text to textView directly.

@interface ViewController () {
    __weak IBOutlet UITextView *textView;
    CGPoint lastContentOffset;
    NSTimer *autoscrollTimer;
    BOOL revertAnyContentOffsetChanges;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [textView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)dealloc
{
    [textView removeObserver:self forKeyPath:@"contentOffset"];
}

- (void)setTextPreservingContentOffset:(NSString *)text
{
    lastContentOffset = textView.contentOffset;
    [autoscrollTimer invalidate];
    revertAnyContentOffsetChanges = YES;
    textView.text = text;
    autoscrollTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(enableScrolling:) userInfo:nil repeats:NO];
    autoscrollTimer.tolerance = 1;
}

- (void)enableScrolling:(NSTimer *)timer
{
    revertAnyContentOffsetChanges = NO;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (revertAnyContentOffsetChanges && [keyPath isEqualToString:@"contentOffset"] && [[change valueForKey:@"new"] CGPointValue].y != lastContentOffset.y) {
        [textView setContentOffset:lastContentOffset animated:NO];
    }
}

@end

Solution 34 - Iphone

The default behaviour of the textview is the scroll to the bottom, as it enables the users to continue editing.

Setting the textview offset as its inset's top value solves it for me.

override func viewDidLayoutSubviews() {  
    super.viewDidLayoutSubviews()  
    self.myTextView.contentOffset.y = -self.myTextView.contentInset.top  
}

Note: My textView was embedded inside a navigation controller.

Solution 35 - Iphone

The answer by ChallengerGuy fully solved my problem once I added a brief delay before re-enabling scrolling. Prior to adding the delay, my PageController pages were all fixed except for the first page, which would also fix itself after any user interaction with the page. Adding the delay fixed the UITextView scroll position of that first page as well.

override open func viewDidLoad() {
    super.viewDidLoad()
    textView.isScrollEnabled = false
    textView.text = textToScroll            
   ... other stuff
}

override open func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    Central().delay(0.5) {
        self.textView.isScrollEnabled = true
    }
}

(The delay function in my Central() file.)

func delay(_ delay: Double, closure:@escaping ()->()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        closure()
    }
}

Solution 36 - Iphone

For Swift I think the cleanest way to do this is to subclass UITextView and use this little trick

import UIKit

class MyTextView: UITextView {
    override var text: String! {
        willSet {
            isScrollEnabled = false
        } didSet {
            isScrollEnabled = true
        }
    }
}

Solution 37 - Iphone

Solution for Xcode 8.0. Swift 2.3. Works for interface build and can be easily modified to work programmatically.

class MBTextView: UITextView
{
    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
        setupView()
    }
    
    func setupView() {}
}

class MBStartFromTopTV: MBTextView
{
    override func setupView()
    {
        // some custom code
    }
    
    override func drawRect(rect: CGRect)
    {
        super.drawRect(rect)
        setContentOffset(CGPoint.zero, animated: false)
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSam StewartView Question on Stackoverflow
Solution 1 - IphoneWayne LoView Answer on Stackoverflow
Solution 2 - Iphonenerd2knowView Answer on Stackoverflow
Solution 3 - IphonemiguelView Answer on Stackoverflow
Solution 4 - IphoneRyanTCBView Answer on Stackoverflow
Solution 5 - IphoneMariaView Answer on Stackoverflow
Solution 6 - IphoneDrew S.View Answer on Stackoverflow
Solution 7 - IphoneBenjamin SussmanView Answer on Stackoverflow
Solution 8 - IphoneChallengerGuyView Answer on Stackoverflow
Solution 9 - IphoneageorgiosView Answer on Stackoverflow
Solution 10 - IphoneMacInnisView Answer on Stackoverflow
Solution 11 - IphoneMaksym HorbenkoView Answer on Stackoverflow
Solution 12 - IphonegargView Answer on Stackoverflow
Solution 13 - IphoneJohn BallingerView Answer on Stackoverflow
Solution 14 - IphoneAechoLiuView Answer on Stackoverflow
Solution 15 - IphoneerkancanView Answer on Stackoverflow
Solution 16 - IphoneCharlie SeligmanView Answer on Stackoverflow
Solution 17 - IphonePatriciaView Answer on Stackoverflow
Solution 18 - IphoneGéza MiklóView Answer on Stackoverflow
Solution 19 - IphoneglaceView Answer on Stackoverflow
Solution 20 - IphoneMarco LeongView Answer on Stackoverflow
Solution 21 - IphoneHermann KleckerView Answer on Stackoverflow
Solution 22 - IphoneAlbert ZouView Answer on Stackoverflow
Solution 23 - IphoneKahngView Answer on Stackoverflow
Solution 24 - IphoneshylaView Answer on Stackoverflow
Solution 25 - IphoneStephen RoweView Answer on Stackoverflow
Solution 26 - IphoneSaRaVaNaN DMView Answer on Stackoverflow
Solution 27 - IphoneIgorView Answer on Stackoverflow
Solution 28 - IphonePaul T.View Answer on Stackoverflow
Solution 29 - IphoneNikolay ShubenkovView Answer on Stackoverflow
Solution 30 - IphoneDavid.Chu.caView Answer on Stackoverflow
Solution 31 - Iphonehawmack13View Answer on Stackoverflow
Solution 32 - Iphoneuser5104628View Answer on Stackoverflow
Solution 33 - IphoneTerryView Answer on Stackoverflow
Solution 34 - IphoneRevanth KausikanView Answer on Stackoverflow
Solution 35 - IphoneWayne HendersonView Answer on Stackoverflow
Solution 36 - IphoneAdamView Answer on Stackoverflow
Solution 37 - IphoneDarkwonderView Answer on Stackoverflow