使用自动布局扩展为文本的UITextView

我有一个完全使用自动布局编程的视图。 我有一个UITextView在视图中间的项目上方和下方。 一切正常,但我希望能够扩大UITextView文本添加。 这应该把它下面的所有东西都拉下来。

我知道如何做到这一点“弹簧和支柱”的方式,但有没有这样做的汽车布局方式? 我能想到的唯一方法是在每次需要增长时删除和重新添加约束。

包含UITextView的视图将通过AutoLayout的setBounds分配其大小。 所以,这就是我所做的。 超级视图最初设置了所有其他约束,最后我为UITextView的高度添加了一个特殊的约束条件,并将其保存在一个实例variables中。

  _descriptionHeightConstraint = [NSLayoutConstraint constraintWithItem:_descriptionTextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.f constant:100]; [self addConstraint:_descriptionHeightConstraint]; 

setBounds方法中,我改变了常量的值。

 -(void) setBounds:(CGRect)bounds { [super setBounds:bounds]; _descriptionTextView.frame = bounds; CGSize descriptionSize = _descriptionTextView.contentSize; [_descriptionHeightConstraint setConstant:descriptionSize.height]; [self layoutIfNeeded]; } 

刚刚进入这个问题,我不知道我是否脱离主题,但如果您将您的UITextView设置为“不”滚动:

 [textView setScrollEnabled:NO]; 

它将能够计算内在尺寸,从而允许自动布局自行移动其他元素,然后由您来设置约束。

(在iOS 7上)

UITextView不提供一个intrinsicContentSize,所以你需要inheritance它并提供一个。 要使其自动增长,请使layoutSubviews中的intrinsicContentSize无效。 如果您使用默认的contentInset(我不build议)以外的任何东西,您可能需要调整intrinsicContentSize计算。

 @interface AutoTextView : UITextView @end 

 #import "AutoTextView.h" @implementation AutoTextView - (void) layoutSubviews { [super layoutSubviews]; if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize])) { [self invalidateIntrinsicContentSize]; } } - (CGSize)intrinsicContentSize { CGSize intrinsicContentSize = self.contentSize; // iOS 7.0+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) { intrinsicContentSize.width += (self.textContainerInset.left + self.textContainerInset.right ) / 2.0f; intrinsicContentSize.height += (self.textContainerInset.top + self.textContainerInset.bottom) / 2.0f; } return intrinsicContentSize; } @end 

你可以通过故事板,只是禁用“滚动启用”:)

故事板

我发现,在您仍然需要将isScrollEnabled设置为true以允许合理的UI交互的情况下,这并不罕见。 一个简单的例子是当你想允许自动扩展文本视图,但仍然限制在UITableView合理的最大高度。

这里是UITextView的一个子类我已经提出了允许自动扩展自动布局,但是你仍然可以限制到最大高度,哪个将pipe理视图是否滚动取决于高度。 默认情况下,如果以这种方式设置约束,视图将无限期地展开。

 import UIKit class FlexibleTextView: UITextView { // limit the height of expansion per intrinsicContentSize var maxHeight: CGFloat = 0.0 private let placeholderTextView: UITextView = { let tv = UITextView() tv.translatesAutoresizingMaskIntoConstraints = false tv.backgroundColor = .clear tv.isScrollEnabled = false tv.textColor = .disabledTextColor tv.isUserInteractionEnabled = false return tv }() var placeholder: String? { get { return placeholderTextView.text } set { placeholderTextView.text = newValue } } override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) isScrollEnabled = false autoresizingMask = [.flexibleWidth, .flexibleHeight] NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: Notification.Name.UITextViewTextDidChange, object: self) placeholderTextView.font = font addSubview(placeholderTextView) NSLayoutConstraint.activate([ placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor), placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor), placeholderTextView.topAnchor.constraint(equalTo: topAnchor), placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var text: String! { didSet { invalidateIntrinsicContentSize() placeholderTextView.isHidden = !text.isEmpty } } override var font: UIFont? { didSet { placeholderTextView.font = font invalidateIntrinsicContentSize() } } override var contentInset: UIEdgeInsets { didSet { placeholderTextView.contentInset = contentInset } } override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize if size.height == UIViewNoIntrinsicMetric { // force layout layoutManager.glyphRange(for: textContainer) size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom } if maxHeight > 0.0 && size.height > maxHeight { size.height = maxHeight if !isScrollEnabled { isScrollEnabled = true } } else if isScrollEnabled { isScrollEnabled = false } return size } @objc private func textDidChange(_ note: Notification) { // needed incase isScrollEnabled is set to true which stops automatically calling invalidateIntrinsicContentSize() invalidateIntrinsicContentSize() placeholderTextView.isHidden = !text.isEmpty } } 

作为奖励,支持包含类似于UILabel的占位符文本。

你也可以做到这一点,而不需要UITextView 。 看看我的答案如何在iOS 7上调整UITextView的内容大小?

使用这个expression式的值:

 [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height 

更新UILayoutConstraint的高度UILayoutConstraint

对于喜欢自动布局的人来说,这是一个解决scheme:

在大小检查员:

  1. 将内容压缩电阻优先级设置为1000。

  2. 通过在约束中单击“编辑”降低约束高度的优先级。 只要less于1000。

在这里输入图像说明

在属性检查器中:

  1. 取消选中“滚动已启用”

重要的是要注意:

由于UITextView是UIScrollView的子类,因此它受UIViewController的自动调整ScrollViewInsets属性的支配。

如果您正在设置布局,并且TextView是UIViewControllers层次结构中的第一个子视图,那么它将修改contentInsets,如果automaticAdjustsScrollViewInsets为true,则有时会导致自动布局中的意外行为。

因此,如果您在自动布局和文本视图时遇到问题,请尝试在视图控制器上automaticallyAdjustsScrollViewInsets = false设置automaticallyAdjustsScrollViewInsets = false或在层次结构中向前移动textView。

顺便说一句,我build立了一个扩展UITextView使用一个子类和重写内在的内容大小。 我在UITextView中发现了一个bug,你可能想在自己的实现中进行调查。 这是问题:

如果您一次只键入单个字母,则展开的文本视图会逐渐缩小以容纳不断增长的文本。 但是,如果你粘贴了一堆文本,它不会长大,但文本会向上滚动,顶部的文本是不可见的。

解决方法:覆盖setBounds:在你的子类中。 由于某种未知的原因,粘贴导致了bounds.origin.y的价值是非zee(我看到的每种情况都是33)。 所以我推翻了setBounds:始终将bounds.origin.y设置为零。 解决了这个问题。

 Obj C: #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (nonatomic) UITextView *textView; @end #import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize textView; - (void)viewDidLoad{ [super viewDidLoad]; [self.view setBackgroundColor:[UIColor grayColor]]; self.textView = [[UITextView alloc] initWithFrame:CGRectMake(30,10,250,20)]; self.textView.delegate = self; [self.view addSubview:self.textView]; } - (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; } - (void)textViewDidChange:(UITextView *)txtView{ float height = txtView.contentSize.height; [UITextView beginAnimations:nil context:nil]; [UITextView setAnimationDuration:0.5]; CGRect frame = txtView.frame; frame.size.height = height + 10.0; //Give it some padding txtView.frame = frame; [UITextView commitAnimations]; } @end