テキストを変更するたびにUITextViewを強制的に最上部にスクロールするにはどうすればよいですか?


99

OK、私はに問題がありUITextViewます。ここに問題があります:

にテキストを追加しますUITextView。次に、ユーザーはダブルクリックして何かを選択します。次にUITextView(上のようにプログラムで)テキストを変更しUITextView 、カーソルがあるページの一番下までスクロールします。

ただし、それはユーザーがクリックした場所ではありません。UITextViewユーザーがクリックした場所に関係なく、常に一番下までスクロールします。

それで、これが私の質問です:UITextViewテキストを変更するたびに、どのようにして強制的に最上部にスクロールしますか?私が試したcontentOffsetscrollRangeToVisible。どちらも機能しません。

任意の提案をいただければ幸いです。

回答:


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

これでうまくいきます。

Swiftバージョン(Xcode 9.3上のiOS 11を搭載したSwift 4.1):

note.setContentOffset(.zero, animated: true)

13
私を助けてはいけない。
ドミトリー2014年

6
これは機能しますが、なぜこれがとにかく必要なのですか?Appleのスクロールロジックには改良が必要です。ほとんどの場合、フープジャンプが多すぎて、自動的に動作するように動作することができません。
John Contarino、2015年

4
私のiOS 9では動作しますが、それ以前には動作しませんviewDidAppear()
Murray Sagal

4
あなたは()viewDidLayoutSubviewsでそれを呼び出すことで、この先にviewDidAppear()よりも呼び出すことができます
アーカディアットザボブを

テーブルのセルにテキストビューを追加したので、これを行のセルに設定しましたが機能しません
Chandni

67

誰もがiOSの8でこの問題を持っている場合、私はちょうど設定ことがわかっUITextView'stextプロパティを、その後の呼び出しscrollRangeToVisibleNSRangelocation:0length:0、働いていました。テキストビューを編集できなかったため、選択可能と選択不可の両方をテストしました(どちらの設定も結果に影響しませんでした)。これがSwiftの例です:

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

おかげで、上記のどれもうまくいきませんでした。あなたのサンプルはしました。
user1244109

これは機能しますが、無効にする必要があるスクロールアニメーションが表示されます。スクロールを無効にし、@ miguelが提案するように再度有効にすることをお勧めします
Warpzit

Objective-C:[myTextView scrollRangeToVisible:NSMakeRange(0、0)];
ステートフル2016年

これはもう機能していないようです。iOS 9およびXcode 7.3.1:/
wasimsandhu

Swift 3 Stllが動作します!:)この行をUITextFielDelegateメソッドにコピーするだけです。`func textViewDidEndEditing(_ textView:UITextView){textView.scrollRangeToVisible(NSRange(location:0、length:0))}`
lifewithelliott

46

そして、これが私の解決策です...

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

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

5
私はこのソリューションは好きではありませんが、少なくともiOS 9ベータ5以降では、これが唯一の機能です。他のほとんどのソリューションは、テキストビューがすでに表示されていると想定しています。私の場合は常にそうだとは限らないので、表示されるまでスクロールを無効にしておく必要があります。
robotspacer 2015

1
これが私にとっても唯一有効だった。iOS 9、編集不可のテキストビューを使用。
SeanR 2015年

1
これは、属性付き文字列を使用したiOS 8の編集不可能なテキストビューで機能します。in viewWillAppear-機能しません
Tina Zh

@robotspacerに同意します。そして、この解決策はまだ私のために働く唯一のものです。
lerp90 2016

私にとって、このソリューションは短い(属性付きの)テキストでのみ機能しました。より長いテキストがある場合、UITextViewスクロールバグは存続し続けます:-)私の(少し醜い)解決策は、2秒の遅延でdispatch_afterを使用してスクロールを再度有効にすることでした。UITextViewは、テキストレイアウトを実行するのに少し時間がかかるようです...
LaborEtArs

38

呼び出す

scrollRangeToVisible(NSMakeRange(0, 0))

動作しますが、それを呼び出します

override func viewDidAppear(animated: Bool)

4
私はviewDidAppearでしか機能しなかったと確信しています。
エリックf。

2
これは私のために働く唯一のものです。どうも。しかし、私が少し迷惑なアニメーションを無効にするにはどうすればよいですか?
ロックハンマー

これは機能しますが、簡単な「ジャンプ」を示さないので、@ Mariaのソリューションの方が優れています。
Sipke Schoorstra 2017

1
最後のコメントを取り戻します。それはiOSの10とiOS 11の両方で動作としてマリアの答え@ iOSの11でうまく動作しないのに対し、これは、私にとって最高の答えです
Sipke Schoorstra

@SipkeSchoorstra、以下の私の回答を参照して、ジャンプなしで動作するようにしてください(KVOを使用)。
テリー

29

HTMLでattributedStringを使用しており、テキストビューは編集できません。コンテンツオフセットの設定も機能しませんでした。これは私にとってうまくいきました:スクロールを無効にして、テキストを設定してから、再度スクロールを有効にしてください

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

tableViewCell内にtextViewがありました。この場合、textViewは約にスクロールされました。50%。このソリューションは、論理的なものと同じくらい簡単です。ありがとう
Julian F.Weinert、2015

1
@ JulianF.WeinertこれはiOS10では機能しないようです。tableViewCellのtextViewの場合と同じ状況です。textViewの編集機能を使用していません。スクロール可能なテキストが欲しいだけです。しかし、textViewは、あなたが言ったように常に約50%までスクロールされます。また、この投稿でsetContentOffsetとscrollRangeToVisibleを設定することで、他のすべての提案も試しました。動作しません。これを機能させるために他に何かしたことがありますか?
JeffB6688

@ JeffB6688ごめんなさい、いいえ。それは本当にずっと前のことです...「新しい」iOSリリースのもう1つの奇妙なものかもしれません。
ジュリアンF.ウェイナート2017年

しかし残念ながらiOS 11.2では完全ではありません。iOSの10とiOSの両方で11 Onlu @RyanTCBの答えの作品
Sipke Schoorstra

これまでのところ、iOS 9、10、11では問題なく機能しますがviewDidAppear、より広範な深刻な修正の一部として有効にする必要がありました
MadProgrammer

19

これらの解決策はどれも私にとってうまくいかなかったので、解決策をまとめるのにあまりにも多くの時間を無駄にしたので、これは最終的にそれを解決しました。

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

これは、これがこのコントロールのデフォルトの動作ではないことは本当にばかげています。

これが他の誰かの助けになることを願っています。


2
これはとても役に立ちました!私も追加する必要がありました:self.automaticallyAdjustsScrollViewInsets = NO; UITextViewを含むビューに追加して、完全に機能するようにします。
jengelsma 2016年

私はそれをSWIFT 3でのみ機能させself.textView.contentOffset.y、この回答に基づいて別のオフセット計算を行いました:stackoverflow.com/a/25366126/1736679
Efren

Swift 3の最新の構文に適応した後、私のために働いた
hawmack13

これがどれほどクレイジーであるかに完全に同意しますか?ソリューションに感謝
ajonno

17

あなたの質問を理解したかどうかはわかりませんが、ビューを上にスクロールするだけですか?もしそうなら

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


1
@SamStewartリンクが壊れているようですが、常に私のアカウントページにアクセスしています(すでにログインしています)。そこで提供されているソリューションに関する詳細情報を提供できますか?
公証人2017

@uliwitnessこれは非常に古い情報です。8年前の投稿ではなく、より最新のリソースを試すことをお勧めします。iOS APIは、この8年間で劇的に変化しました。
ベンジャミンサスマン2017

1
新しい情報を見つけようとしました。私がそれをどこに見つけることができるか知っているなら、私はそれを感謝します。それでも同じ問題のようです:(
uliwitness

13

iOS 10とSwift 3の場合:

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

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

SwiftとiOSの最新バージョンで問題が発生しているかどうかはわかりません。


フォローするのに最適なソリューションです!
iChirag 2017

UIPageViewControllerによって制御されるUIViewのUITextViewでスクロールの問題が発生しています。(これらのページには、ヘルプ/バージョン情報画面が表示されます。)ソリューションは、最初のページを除くすべてのページで機能します。任意のページにスワイプして最初のページに戻ると、その最初のページのスクロール位置が固定されます。別の.isScrollEnabled = FalseをviewWillAppearに追加してみましたが、効果がありませんでした。最初のページが他のページと異なる動作をする理由に困惑しています。
ウェインヘンダーソン

[更新]解決しました!.isScrollEnabled = trueトリガーが最初の読み込みの問題を修正する前に、0.5秒の遅延を導入します。
ウェインヘンダーソン

非常に素晴らしい 。。。
Maysam R 2018

魅力のように働いた。私のために働く唯一の解決策。あなたは男です。
Lazy Ninja

10

テキストビューが正しく表示され、ユーザーがスクロールアニメーションを体験することなく、更新された回答があります。

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

メインキューで実行しているのはなぜですか、sine viewWillAppearがメインキューで実行されていますか?
karthikPrabhu Alagu 2017

1
私が思い出すにはあまりにも多くの月前ですが、ここで取り上げられています:stackoverflow.com/a/17490329/4750649
MacInnis 2017

8

これはiOS 9リリースでの動作で、textViewが画面に表示される前に上にスクロールされます。

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

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

1
これでうまくいきました。ちなみに、それは私に非常に重要なことを教えてくれました。番号?
Sjakelien 2017

8

それが私がやった方法です

スウィフト4:

extension UITextView {

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

 }

これが私にとって最良の結果を生み出す唯一の方法です。私は賛成です。
smoothBlue

7

これは私のために働いた

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

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

これは非常に奇妙です。ほとんどのデバイスでDispatchQueue.main.async(...は、通話をラップする必要はありませんが、何らかの理由で、iPhone 5s(iOS 12.1など)では、ディスパッチしない限り機能しません。しかし、デバッガのコールスタックによって報告されているように、すべてのケースでviewWillAppear() すでにメインキュー実行されています ...-WTF、Apple ???
Nicolas Miari、

2
メインキューですべてのイベントを実行しているのに、なぜメインキューで明示的にディスパッチする必要があるのか​​についても疑問に思いました。しかし、これは私にとっては
うまくいっ

6

これを試して、カーソルをテキストの上部に移動します。

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

それがどうなるか見てください。すべてのイベントが発生した後、またはこれまでに何を実行した後でも、必ずこれを呼び出してください。


試してみたが、運はなかった。firstResponderのようなものです。他に何か提案はありますか?
サムスチュワート、

カーソル位置に同じ問題があり、それは私のために働いた...そのための素晴らしい+1
ダヴァル2013

下から上にアニメーションを取得していますが、それを削除する方法は必要ありません。
Dhaval

2
上にスクロールしません(数ピクセル以下)。
ドミトリー

6

私の問題は、ビューが表示されたときにtextViewを上にスクロールするように設定することです。iOS 10.3.2およびSwift 3の場合。

解決策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)
}

解決策2:

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

5

以前の答えを組み合わせると、今はうまくいくはずです:

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が私にとってどれほどばかげて働いたのか。でも削除できましscrollRangeToVisibleた。
ジョーダンH

NSRange(0,0)をNSMakeRange(0,0)に置き換える必要があります
RobP

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

このコード行は私にとってはうまくいきます。


5

このコードを使用できます

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

4

Swift 2の回答:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

これにより、設定後にtextViewがテキストの最後までスクロールしなくなります。


4

Swift 3の場合:

  • viewWillLayoutSubviewsは変更を行う場所です。viewWillAppearも機能するはずですが、論理的には、レイアウトviewWillLayoutSubviewsで実行する必要があります。

  • メソッドに関しては、scrollRangeToVisiblesetContentOffsetの両方が機能します。ただし、setContentOffsetを使用すると、アニメーションをオフまたはオンにすることができます。

想定UITextViewをとして命名されyourUITextView。これがコードです:

// 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は、ビューのサイズ変更時に呼び出されます。それは一般的にあなたが望むものではありません。
公証人2017

4

次のviewDidLayoutSubviewsを呼び出す必要がありました(viewDidLoad内での呼び出しは早すぎました)。

    myTextView.setContentOffset(.zero, animated: true)

がすべてのシナリオで機能するわけではありません
Raja Saad

3

これでうまくいきました。これはRyanTCBの回答に基づいていますが、同じソリューションのObjective-Cバリアントです。

- (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は、ビューのサイズ変更時に呼び出されます。それは一般的にあなたが望むものではありません。
公証人2017

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

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappearユーザーが気付く前に即座にそれを行いますが、ViewDidDisappearセクションはそれを遅延してアニメーション化します。
  • [mytextView setContentOffset:CGPointZero animated:YES]; 時々動作しない(理由はわかりません)ので、むしろscrollrangetovisibleを使用してください。

3

解決した問題:iOS = 10.2 Swift 3 UITextView

私は次の行を使用しました:

displayText.contentOffset.y = 0

1
動作しました(iOS 10.3)!内部に追加したviewDidLayoutSubviewsので、画面が回転したときに発生します。そうしないと、コンテンツのオフセットがずれます。
子犬

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

詳細を編集してください。
回路図2015年

2

私は問題を抱えていましたが、私の場合、textviewはいくつかのカスタムビューのサブビューであり、それをViewControllerに追加しました。解決策はどれも私にとってはうまくいきませんでした。この問題を解決するために、0.1秒の遅延が追加され、スクロールが有効になりました。それは私のために働いた。

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

私にとってはこのコードはうまく機能します:

    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

正確な位置にスクロールして画面の上部に表示するには、次のコードを使用します。

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

contentOffset.yを設定すると、テキストの最後までスクロールし、scrollRangeToVisibleはscrollToLocationの値までスクロールします。これにより、scrollViewの1行目に必要な位置が表示されます。


1

iOS 9の私にとっては、scrollRangeToVisibleメソッドを持つ属性付き文字列で機能しなくなりました(1行は太字フォントで、他の行は通常のフォントでした)

だから私は使用しなければなりませんでした:

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

ここで遅延は:

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

次の2行のソリューションを使用してみてください。

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

これでうまくいきました。私のUITextViewはUICollectionVewCell内にありました。レイアウトが適切に計算されない限り、scrollRangeToVisibleが間違った場所にスクロールするのではないかと思います
John Fowler

1

これが私のコードです。正常に動作します。

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()
      }
  }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.