加载时 UITextView 未滚动到顶部
UITextView is not scrolled to top when loaded
当我有未填充 UITextView 的文本时,它会按预期滚动到顶部。当文本多于屏幕无法容纳时,UITextView 会滚动到文本的中间,而不是顶部。
以下是一些可能相关的详细信息:
在 viewDidLoad 中为 UITextView 的顶部和底部提供一些填充:
self.mainTextView.textContainerInset = UIEdgeInsetsMake(90, 0, 70, 0);
UITextView 使用自动布局将其锚定在距屏幕顶部、底部和每一侧 20 像素的位置(在 IB 中完成),以允许不同的屏幕尺寸和方向。
加载后我仍然可以用手指滚动它。
编辑
我发现删除自动布局约束然后修复宽度似乎只能解决问题,但仅限于该屏幕宽度。
UITextView是UIScrollView的子类,所以可以使用它的方法。如果您只想确保它滚动到顶部,那么在添加文本的任何地方尝试:
[self.mainTextView setContentOffset:CGPointZero animated:NO];
编辑:AutoLayout 与任何类型的滚动视图变得不稳定快速。设置固定宽度解决这个问题并不奇怪。如果它在 -viewDidLayoutSubviews
中不起作用,那就很奇怪了。手动设置布局约束可能会起作用。首先在IB中创建约束:
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeightConstraint;
然后在 ViewController
-(void)updateViewConstraints {
self.textViewWidthConstraint.constant = self.view.frame.size.width - 40.0f;
self.textViewHeightConstraint.constant = self.view.frame.size.height - 40.0f;
[super updateViewConstraints];
}
可能仍然需要在 -viewDidLayoutSubviews
中设置 ContentOffset。
(另一种方法是在 textView 及其 superView 之间为“'equal' 宽度”和“'equal' 高度”创建布局约束,常数为“-40”。它是只有 'equal' 如果常量为零,否则它会按常量调整。但是因为您只能将此约束添加到约束两个视图的视图,所以您不能在 IB 中执行此操作。)
您可能会问自己,如果我必须这样做,AutoLayout 有什么意义?我深入研究了 AutoLayout,这是一个很好的问题。
我有同样的问题!重置为建议的约束并放置 (y offset)
@IBOutlet weak var textContent: UITextView!
override func viewDidLoad() {
textContent.scrollsToTop = true
var contentHeight = textContent.contentSize.height
var offSet = textContent.contentOffset.x
var contentOffset = contentHeight - offSet
textContent.contentOffset = CGPointMake(0, -contentOffset)
}
将以下函数添加到您的视图控制器class...
Swift 3
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(.zero, animated: false)
}
Swift 2.1
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(CGPointZero, animated: false)
}
Objective C
- (void)viewDidLayoutSubviews {
[self.mainTextView setContentOffset:CGPointZero animated:NO];
}
如果你不想打乱约束:
override func updateViewConstraints() {
super.updateViewConstraints()
}
override func viewDidLayoutSubviews() {
self.textLabel.setContentOffset(CGPointZero, animated: false)
}
Swift
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
Objective-C
[self.textView scrollRangeToVisible:(NSMakeRange(0, 0))];
对于 iOS9 及更高版本,甚至在 viewWillAppear: 上的文本视图都带有 CGRect(0,0,1000,1000)。为了让它工作,你必须调用 viewWillAppear:
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
// * Your code here
之后,textview 将具有正确的 CGRect 数据,您可以执行您可能需要的任何滚动操作。
将代码放在 viewDidLayoutSubviews 和 viewWillLayoutSubviews 中的问题是这些方法被调用了很多次(在设备旋转、调整视图大小等过程中)。如果您正在从文本视图中阅读内容,并且旋转设备,您希望您正在查看的内容部分保留在屏幕上。您不希望它滚动回到顶部。
不要将内容滚动到顶部,而是尝试将文本视图的 scrollEnabled 属性 设置为 NO (false),然后在 viewDidAppear.
中将其重新打开
这是一个有趣的错误。在我们的项目中,这只发生在屏幕尺寸为 iPhone 5
的设备上。看起来 textview contentOffset
在视图控制器生命周期的某个时刻发生了变化。在 viewDidLoad
和 viewWillAppear
中,textview 的 contentOffset
是 0,0
,到 viewDidAppear
时,它发生了变化。您可以在 viewWillLayoutSubviews
中看到它的发生。约束似乎设置正确。
这将确保您不会在需要时调用滚动方法:
if textView.contentOffset.y > 0 {
textView.contentOffset = CGPoint(x: 0, y: 0)
// Or use scrollRectToVisible, scrollRangeToVisible, etc.
}
Swift
override func viewDidLoad() {
super.viewDidLoad()
textView.isScrollEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.isScrollEnabled = true
}
对我来说,这以不同的方式起作用,我尝试了上面提到的所有方法,但 none 在 func viewWillAppear(_ animated: Bool)
中起作用。这最终使 textView 向上滚动,并且在 func viewDidAppear(_ animated: Bool)
中它会在屏幕出现后滚动。
下面对我有用,但在键盘上下移动时遇到了一些与约束相关的问题。
override func viewDidLayoutSubviews() {
self.textView.setContentOffset(.zero, animated: false)
}
低于预期:
override func viewDidLoad() {
super.viewDidLoad()
self.textView.scrollsToTop = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
self.textView.setContentOffset(.zero, animated: false)
}
在视图控制器中的代码中处理这个问题似乎是一个糟糕的主意,因为:A. 视图控制器没有犯任何错误或做错任何事情,B,如果你有多个视图控制器使用错误滚动的文本视图,您最终会得到冗余代码。解决方案应该是编写存在于文本视图中的代码class。我的解决方案适用于 Interface Builder,我只是 select UITextView 的自定义 class 并使用此 class:
import Foundation
import UIKit
class TopTextView: UITextView {
var scrolled = false
override func layoutSubviews() {
super.layoutSubviews()
if scrolled { return }
setContentOffset(.zero, animated: false)
scrolled = true
}
}
这对我有用。我碰巧有一个带有子视图的视图控制器,其中 UITextView 作为该视图的子视图,而不是 UITextView 作为视图控制器的子视图。如果文本视图位于顶部或底部栏下方,我不知道它的效果如何,但由于没有触及边缘插入,这应该可以。
David Rectors 在 Objective C 中回答:
#import "TopTextView.h"
@implementation TopTextView
bool scrolled = NO;
- (void) layoutSubviews
{
[super layoutSubviews];
if (!scrolled) {
[self setContentOffset:CGPointMake(0, 0) animated:NO];
scrolled = YES;
}
}
@end
当我有未填充 UITextView 的文本时,它会按预期滚动到顶部。当文本多于屏幕无法容纳时,UITextView 会滚动到文本的中间,而不是顶部。
以下是一些可能相关的详细信息:
在 viewDidLoad 中为 UITextView 的顶部和底部提供一些填充:
self.mainTextView.textContainerInset = UIEdgeInsetsMake(90, 0, 70, 0);
UITextView 使用自动布局将其锚定在距屏幕顶部、底部和每一侧 20 像素的位置(在 IB 中完成),以允许不同的屏幕尺寸和方向。
加载后我仍然可以用手指滚动它。
编辑 我发现删除自动布局约束然后修复宽度似乎只能解决问题,但仅限于该屏幕宽度。
UITextView是UIScrollView的子类,所以可以使用它的方法。如果您只想确保它滚动到顶部,那么在添加文本的任何地方尝试:
[self.mainTextView setContentOffset:CGPointZero animated:NO];
编辑:AutoLayout 与任何类型的滚动视图变得不稳定快速。设置固定宽度解决这个问题并不奇怪。如果它在 -viewDidLayoutSubviews
中不起作用,那就很奇怪了。手动设置布局约束可能会起作用。首先在IB中创建约束:
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeightConstraint;
然后在 ViewController
-(void)updateViewConstraints {
self.textViewWidthConstraint.constant = self.view.frame.size.width - 40.0f;
self.textViewHeightConstraint.constant = self.view.frame.size.height - 40.0f;
[super updateViewConstraints];
}
可能仍然需要在 -viewDidLayoutSubviews
中设置 ContentOffset。
(另一种方法是在 textView 及其 superView 之间为“'equal' 宽度”和“'equal' 高度”创建布局约束,常数为“-40”。它是只有 'equal' 如果常量为零,否则它会按常量调整。但是因为您只能将此约束添加到约束两个视图的视图,所以您不能在 IB 中执行此操作。)
您可能会问自己,如果我必须这样做,AutoLayout 有什么意义?我深入研究了 AutoLayout,这是一个很好的问题。
我有同样的问题!重置为建议的约束并放置 (y offset)
@IBOutlet weak var textContent: UITextView!
override func viewDidLoad() {
textContent.scrollsToTop = true
var contentHeight = textContent.contentSize.height
var offSet = textContent.contentOffset.x
var contentOffset = contentHeight - offSet
textContent.contentOffset = CGPointMake(0, -contentOffset)
}
将以下函数添加到您的视图控制器class...
Swift 3
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(.zero, animated: false)
}
Swift 2.1
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(CGPointZero, animated: false)
}
Objective C
- (void)viewDidLayoutSubviews {
[self.mainTextView setContentOffset:CGPointZero animated:NO];
}
如果你不想打乱约束:
override func updateViewConstraints() {
super.updateViewConstraints()
}
override func viewDidLayoutSubviews() {
self.textLabel.setContentOffset(CGPointZero, animated: false)
}
Swift
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
Objective-C
[self.textView scrollRangeToVisible:(NSMakeRange(0, 0))];
对于 iOS9 及更高版本,甚至在 viewWillAppear: 上的文本视图都带有 CGRect(0,0,1000,1000)。为了让它工作,你必须调用 viewWillAppear:
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
// * Your code here
之后,textview 将具有正确的 CGRect 数据,您可以执行您可能需要的任何滚动操作。
将代码放在 viewDidLayoutSubviews 和 viewWillLayoutSubviews 中的问题是这些方法被调用了很多次(在设备旋转、调整视图大小等过程中)。如果您正在从文本视图中阅读内容,并且旋转设备,您希望您正在查看的内容部分保留在屏幕上。您不希望它滚动回到顶部。 不要将内容滚动到顶部,而是尝试将文本视图的 scrollEnabled 属性 设置为 NO (false),然后在 viewDidAppear.
中将其重新打开这是一个有趣的错误。在我们的项目中,这只发生在屏幕尺寸为 iPhone 5
的设备上。看起来 textview contentOffset
在视图控制器生命周期的某个时刻发生了变化。在 viewDidLoad
和 viewWillAppear
中,textview 的 contentOffset
是 0,0
,到 viewDidAppear
时,它发生了变化。您可以在 viewWillLayoutSubviews
中看到它的发生。约束似乎设置正确。
这将确保您不会在需要时调用滚动方法:
if textView.contentOffset.y > 0 {
textView.contentOffset = CGPoint(x: 0, y: 0)
// Or use scrollRectToVisible, scrollRangeToVisible, etc.
}
Swift
override func viewDidLoad() {
super.viewDidLoad()
textView.isScrollEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.isScrollEnabled = true
}
对我来说,这以不同的方式起作用,我尝试了上面提到的所有方法,但 none 在 func viewWillAppear(_ animated: Bool)
中起作用。这最终使 textView 向上滚动,并且在 func viewDidAppear(_ animated: Bool)
中它会在屏幕出现后滚动。
下面对我有用,但在键盘上下移动时遇到了一些与约束相关的问题。
override func viewDidLayoutSubviews() {
self.textView.setContentOffset(.zero, animated: false)
}
低于预期:
override func viewDidLoad() {
super.viewDidLoad()
self.textView.scrollsToTop = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
self.textView.setContentOffset(.zero, animated: false)
}
在视图控制器中的代码中处理这个问题似乎是一个糟糕的主意,因为:A. 视图控制器没有犯任何错误或做错任何事情,B,如果你有多个视图控制器使用错误滚动的文本视图,您最终会得到冗余代码。解决方案应该是编写存在于文本视图中的代码class。我的解决方案适用于 Interface Builder,我只是 select UITextView 的自定义 class 并使用此 class:
import Foundation
import UIKit
class TopTextView: UITextView {
var scrolled = false
override func layoutSubviews() {
super.layoutSubviews()
if scrolled { return }
setContentOffset(.zero, animated: false)
scrolled = true
}
}
这对我有用。我碰巧有一个带有子视图的视图控制器,其中 UITextView 作为该视图的子视图,而不是 UITextView 作为视图控制器的子视图。如果文本视图位于顶部或底部栏下方,我不知道它的效果如何,但由于没有触及边缘插入,这应该可以。
David Rectors 在 Objective C 中回答:
#import "TopTextView.h"
@implementation TopTextView
bool scrolled = NO;
- (void) layoutSubviews
{
[super layoutSubviews];
if (!scrolled) {
[self setContentOffset:CGPointMake(0, 0) animated:NO];
scrolled = YES;
}
}
@end