web-dev-qa-db-ja.com

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

OK、UITextViewに問題があります。問題は次のとおりです。

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

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

私の質問は次のとおりです。テキストを変更するたびにUITextViewを強制的に一番上にスクロールさせるにはどうすればよいですか? contentOffsetscrollRangeToVisibleを試しました。どちらも動作しません。

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

92
Sam Stewart
UITextView*note;
[note setContentOffset:CGPointZero animated:YES];

これは私のためにそれを行います。

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

note.setContentOffset(.zero, animated: true)
144
Wayne Lo

IOS 8でこの問題が発生した場合、UITextView'sテキストプロパティを設定し、scrollRangeToVisibleNSRangeで呼び出し、location:0length:0、働いた。テキストビューは編集できず、選択可能と選択不可能の両方をテストしました(どちらの設定も結果に影響しませんでした)。 Swiftの例:

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

そして、ここに私の解決策があります...

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

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

呼び出し中

scrollRangeToVisible(NSMakeRange(0, 0))

動作しますが、呼び出します

override func viewDidAppear(animated: Bool)
35
RyanTCB

テキストビューを編集できないHTMLでattributedStringを使用していました。コンテンツオフセットの設定も機能しませんでした。これは私のために働いた:スクロールを無効にし、テキストを設定し、スクロールを再び有効にする

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

これらのソリューションはどれも役に立たず、ソリューションをつなぎ合わせるのに時間がかかりすぎたので、最終的には解決しました。

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

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

これが他の人の助けになるといいのですが。

18
Drew S.

私はあなたの質問を理解しているかどうかはわかりませんが、単にビューを一番上までスクロールしようとしていますか?もしそうならあなたはすべき

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

17

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に問題があるかどうかはわかりませんが、.

11
ChallengerGuy

テキストビューが適切に表示され、ユーザーにスクロールアニメーションが表示されない更新された回答があります。

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

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

スウィフト4:

extension UITextView {

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

 }
8
Maksym Horbenko

これは、textViewが画面に表示される前に上にスクロールされるため、iOS 9リリースでどのように機能したかです。

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

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

私の問題は、ビューが表示されたときに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
AechoLiu
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

このコード行は私に適しています。

5
Patricia

前の回答を組み合わせて、今では動作するはずです:

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
5
Géza Mikló

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

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

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

5
John Ballinger

Swift 2回答:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

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

4
glace

In Swift 3 ::

  • viewWillLayoutSubviewsは、変更を加える場所です。 viewWillAppearも機能するはずですが、論理的にはlayoutviewWillLayoutSubviewsで実行する必要があります。

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

ITextViewの名前が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)
}
4
Marco Leong

これは私のために働いた

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

    DispatchQueue.main.async {
      self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
    }
  }
3
Ankit garg
-(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
Kahng

これは私のために働いた。 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
Hermann Klecker

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

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

問題解決:iOS = 10.2 Swift 3 UITextView

次の行を使用しました。

displayText.contentOffset.y = 0
3
Stephen Rowe

私は問題を抱えていましたが、私の場合、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
}
2
SaRaVaNaN DM
-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    [textview setContentOffset:CGPointZero animated:NO];
}
2
Albert Zou
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];
2
shyla

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

    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
Igor

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

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

一部の人々がこの問題で問題を抱えている理由は、TextViewが現れる前にTextViewスクロール位置を設定しようとしているためだと思います。

TextViewがまだ表示されていない場合、スクロールするものはありません。

TextViewが最初に表示されるまで待ってから、次に示すようにviewDidAppearメソッド内でスクロール位置を設定する必要があります。

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

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

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

このコードは問題を解決し、textViewの最後までの不要な自動スクロールを回避するのにも役立ちます。テキストを直接textViewに設定する代わりに、メソッドsetTextPreservingContentOffset:を使用します。

@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
1
Terry

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

    myTextView.setContentOffset(.zero, animated: true)
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
Paul T.

Drew_sのSwift 3バージョンは以前に答えました。これは、他のほとんどの答えを試した後、私のために働いた唯一のことです。

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

これは作業コードのカットアンドペーストでした。 person_descriptionをtextview変数に置き換えます。

1
hawmack13

ChallengerGuyの答えは、スクロールを再び有効にする前に短い遅延を追加すると、私の問題を完全に解決しました。遅延を追加する前に、PageControllerページはすべて、最初のページを除いてすべて修正されました。最初のページは、ユーザーがページを操作した後にも修正されます。遅延を追加すると、その最初のページのUITextViewスクロール位置も修正されました。

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

(私のCentral()ファイルの遅延機能。)

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

Swiftこれを行う最もクリーンな方法は、UITextViewをサブクラス化し、この小さなトリックを使用することだと思います

import UIKit

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