每次更改文本时,如何强制UITextView滚动到顶部?


99

好的,我的有点问题UITextView。这是问题所在:

我在中添加了一些文本UITextView。然后,用户双击以选择某些内容。然后,我更改UITextView(在程序中如上所述)和UITextView 滚动到页面底部有光标的位置。

但是,那不是用户单击的地方。始终滚动到UITextView无论用户单击何处,。

所以这是我的问题:UITextView每次更改文本时,如何强制滚动到顶部?我试过contentOffsetscrollRangeToVisible。都不起作用。

任何建议,将不胜感激。

Answers:


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

这为我做到了。

Swift版本(Xcode 9.3上带有iOS 11的Swift 4.1):

note.setContentOffset(.zero, animated: true)

13
对我没有帮助。
德米特里(Dmitry)

6
这行得通,但是为什么仍然有必要这样做呢?Apple的滚动逻辑需要改进,在大多数情况下,跳跃太多,无法使事情表现为自动运行的样子。
约翰·康塔里诺

4
在iOS 9中对我有效,但不早于viewDidAppear()
Murray Sagal

4
您可以在viewDidLayoutSubviews()中调用它,而不是在viewDidAppear()之前调用
arcady bob

我已在表格的单元格中添加了文本视图,因此我将其设置为行中的单元格,但不起作用
Chandni

67

如果任何人有在iOS 8的这个问题,我发现只需设置UITextView's文本属性,然后调用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)];
有状态的

这似乎不再起作用。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 beta 5开始,这是唯一对我有用的方法。我假设大多数其他解决方案都假设您的文本视图已经可见。就我而言,这并不总是正确的,因此我必须保持滚动禁用,直到出现为止。
robotspacer

1
这也是唯一对我有用的东西。iOS 9,使用不可编辑的textview。
SeanR 2015年

1
这适用于我,带有属性字符串的iOS 8不可编辑textview。在viewWillAppear中-不起作用
Tina Zh

我同意@robotspacer。而且该解决方案仍然是唯一对我有用的解决方案。
lerp90 '16

对我而言,此解决方案仅适用于简短的(归因)文本。当文本较长时,UITextView滚动错误会持续存在:-)我(有点难看)的解决方案是在延迟2秒后使用dispatch_after重新启用滚动。UITextView似乎需要少量时间来进行文本布局...
LaborEtArs

38

呼唤

scrollRangeToVisible(NSMakeRange(0, 0))

可以,但是打电话

override func viewDidAppear(animated: Bool)

4
我认为它仅对viewDidAppear有效。
埃里克f。

2
这是唯一对我有用的。谢谢。但是,如何禁用我觉得有些烦人的动画?
rockhammer '16

尽管这可行,但我@Maria的解决方案更好,因为该解决方案没有显示简短的“跳转”。
Sipke Schoorstra

1
我把我的最后评论回去;这是对我最好的答案,因为它适用于iOS的两个10和11的iOS,而@玛丽亚的回答不能很好地在iOS 11.工作
Sipke Schoorstra

@SipkeSchoorstra,请参阅下面的我的答案,以使其工作无任何跳跃(使用KVO)。
特里

29

我在HTML中使用attributedString,且文本视图不可编辑。设置内容偏移量对我也不起作用。这对我有用:禁用滚动启用,设置文本,然后再次启用滚动

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

我在tableViewCell中有一个textView。在这种情况下,textView滚动到了大约。50%。该解决方案很简单,就像逻辑上一样。谢谢
朱利安·F·韦纳特

1
@ JulianF.Weinert这似乎不适用于iOS10。我和tableViewCell中的textView的情况相同。我没有使用textView的编辑功能。我只想要可滚动的文本。但是,正如您所说,textView始终滚动到大约50%。我还尝试了在本文中设置setContentOffset和scrollRangeToVisible的所有其他建议。没有工作。您可能还需要做其他什么才能使这项工作成功?
JeffB6688'17年

@ JeffB6688对不起,不。那确实是很久以前了……可能是“新” iOS版本的另一个怪癖?
朱利安·F·韦纳特

但不幸的是,这在iOS 11.2上并不完美。两个10的iOS和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 '16

我仅在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链接似乎已断开,总是会转到我的帐户页面(已登录),您能否提供有关他们在此处提供的解决方案的更多信息?
uliwitness

@uliwitness 现在这是非常古老的信息,我建议尝试使用更现代的资源,而不是8年的旧帖子。在过去的8年中,iOS API发生了翻天覆地的变化。
本杰明·萨斯曼

1
我试图找到更新的信息。如果您知道在哪里可以找到它,我将不胜感激。似乎仍然是同一问题:(
uliwitness '17

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

我在由UIPageViewController控制的UIView中遇到UITextView的滚动问题。(这些页面显示了我的“帮助/关于”屏幕。)您的解决方案适用于除第一个页面之外的所有页面。滑动到任何页面并返回第一页都可以固定该第一页的滚动位置。我尝试将另一个.isScrollEnabled = False添加到viewWillAppear中,但这没有任何效果。我很困惑为什么首页的行为与其余页面有所不同。
韦恩·亨德森

[更新]刚刚解决了!在.isScrollEnabled = true触发之前引入0.5秒的延迟可解决首次加载问题。
韦恩·亨德森

非常好 。。。
Maysam R

像魅力一样工作。唯一适合我的解决方案。你是男人
懒忍者'18

10

我有一个更新的答案,它将使textview正确显示,并且用户没有滚动动画。

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

为什么在主队列中执行正弦viewWillAppear在主队列上运行?
karthikPrabhu Alagu'8

1
太多的卫星让我想不起来,但在这里得到了解决:stackoverflow.com/a/17490329/4750649
MacInnis

8

这是它在iOS 9 Release上的工作方式,因此textView在出现在屏幕上之前先在顶部滚动

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

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

1
这成功了。顺便说一句,它教会了我一些非常重要的知识:“ viewwillappear”中的“ view”不仅指self.view,而且显然指的是所有视图。没有?
Sjakelien


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 '19

2
我还想知道,即使我在主队列上运行所有事件,为什么也必须在主队列上显式调度。但这个工作对我来说
安基特·加

6

尝试将光标移动到文本顶部。

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

看看情况如何。确保在触发所有事件或完成所有操作后调用此方法。


尝试过的男人,没有运气。与firstResponder似乎有些关系。还有其他建议吗?
山姆·斯图尔特

光标位置也有同样的问题,它为我工作。。。很棒+1。
Dhaval

我已经从下到上获得了动画,但是我没有要求这样做,所以如何删除它。
Dhaval 2013年

2
它不会滚动到顶部(少一些像素)。
德米特里(Dmitry)2014年

6

我的问题是将view设置为将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在任何视图调整大小时调用。这通常不是您想要的。
uliwitness

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在任何视图调整大小时调用。这通常不是您想要的。
uliwitness

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

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappear会在用户注意到之前立即执行此操作,但是ViewDidDisappearsection将对其进行动画处理。
  • [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

对于我来说,在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

尝试使用以下两行解决方案:

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()
      }
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.