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

tha*_*ris 155 uitextview ios autolayout nslayoutconstraint

我有一个完全使用自动布局编程的视图.我在视图中间有一个UITextView,上面和下面有项目.一切正常,但我希望能够在添加文本时扩展UITextView.当它扩展时,它应该将其下面的所有内容都推下来.

我知道如何以"弹簧和支柱"的方式做到这一点,但是有一种自动布局方式吗?我能想到的唯一方法是每次需要增长时删除并重新添加约束.

vit*_*ter 543

摘要:禁用文本视图的滚动,并且不限制其高度.

要以编程方式执行此操作,请将以下代码放入viewDidLoad:

let textView = UITextView(frame: .zero, textContainer: nil)
textView.backgroundColor = .yellow // visual debugging
textView.isScrollEnabled = false   // causes expanding height
view.addSubview(textView)

// Auto Layout
textView.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
    textView.topAnchor.constraint(equalTo: safeArea.topAnchor),
    textView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
    textView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor)
])
Run Code Online (Sandbox Code Playgroud)

要在Interface Builder中执行此操作,请选择文本视图,取消选中"属性"检查器中的"滚动已启用",然后手动添加约束.

注意:如果您在文本视图的上方/下方有其他视图,请考虑使用a UIStackView来安排所有视图.

  • 适合我(iOS 7 @ xcode 5).需要注意的是,简单地取消选中IB中的"滚动启用"将无济于事.你必须以编程方式执行它. (12认同)
  • 如何在文本视图上设置最大高度,然后在其内容超过文本视图的高度后让文本视图滚动? (10认同)
  • @iOSGeek只需创建一个UITextView子类,然后像这样覆盖`intrinsicContentSize`--(CGSize)intrinsicContentSize {CGSize size = [super internalContentSize]; 如果(self.text.length == 0){size.height = UIViewNoIntrinsicMetric; }返回大小;}`。告诉我它是否对您有用。我自己没有测试。 (3认同)
  • 这个答案是黄金.使用Xcode 9,如果禁用IB中的复选框,它也可以使用. (3认同)
  • 设置此属性并将 UITextView 放在 UIStackView 中。Voilla - 自行调整 textView 大小!) (2认同)

Mic*_*lis 40

对于喜欢通过自动布局完成所有操作的人来说,这是一个解决方案:

在尺寸检查器中:

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

  2. 通过单击约束中的"编辑"来降低约束高度的优先级.只需使它低于1000.

在此输入图像描述

在属性检查器中:

  1. 取消选中"滚动已启用"


小智 39

UITextView不提供intrinsicContentSize,因此您需要对其进行子类化并提供一个.要使其自动增长,请使layoutSubviews中的intrinsicContentSize无效.如果您使用默认contentInset以外的任何内容(我不建议这样做),您可能需要调整intrinsicContentSize计算.

@interface AutoTextView : UITextView

@end
Run Code Online (Sandbox Code Playgroud)
#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
Run Code Online (Sandbox Code Playgroud)

  • 这似乎在**iOS 7.1中不再是必需的**要修复并提供向后兼容性,请将条件包装在` - (void)layoutSubviews`中,并将`_(CGSize)intrinsicContentSize`中的代码用`version> =包装_all_ 7.0f && version <7.1f` +`else返回[super intrinsicContentSize];` (6认同)

anc*_*jic 27

包含UITextView的视图将setBounds通过AutoLayout 分配其大小.所以,这就是我所做的.superview最初设置了应有的所有其他约束,最后我为UITextView的高度设置了一个特殊约束,并将其保存在实例变量中.

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

[self addConstraint:_descriptionHeightConstraint];
Run Code Online (Sandbox Code Playgroud)

在该setBounds方法中,我然后更改了常量的值.

-(void) setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    _descriptionTextView.frame = bounds;
    CGSize descriptionSize = _descriptionTextView.contentSize;

    [_descriptionHeightConstraint setConstant:descriptionSize.height];

    [self layoutIfNeeded];
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以更新UITextViewDelegate的'textViewDidChange:'中的约束 (4认同)
  • 好的解决方案 请注意,您还可以在Interface Builder中创建约束,并使用插座连接它.结合使用`textViewDidChange`,这应该节省一些麻烦的子类和编写代码... (2认同)
  • 请记住,以编程方式向UITextView添加文本时不会调用`textViewDidChange:`. (2认同)

DaN*_*LtR 25

你可以通过storyboard完成,只需禁用"Scrolling Enabled":)

故事板

  • 关于这个问题的最佳答案 (2认同)

小智 15

我发现在你可能仍然需要将isScrollEnabled设置为true以允许合理的UI交互的情况下,这并不完全不常见.一个简单的例子就是当你想允许自动扩展文本视图但仍然将它的最大高度限制在UITableView中的合理范围内时.

这是我提出的UITextView的子类,它允许使用自动布局进行自动扩展,但是你仍然可以约束到最大高度,并根据高度管理视图是否可滚动.默认情况下,如果您以这种方式设置约束,视图将无限扩展.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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


fum*_*007 11

我看到多个答案建议直接关闭scrollEnabled。这是最好的解决方案。我\xe2\x80\x99m 写这个答案是为了解释它为什么有效。

\n

UITextViewintrinsicContentSize仅当 时才实现该属性scrollEnabled == NO。getter 方法的反汇编如下所示:

\n
- (CGSize)intrinsicContentSize {\n  if (self.scrollEnabled) {\n    return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);\n  } else {\n    // Calculate and return intrinsic content size based on current width.\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这意味着您只需要确保文本视图的宽度受到足够的限制,然后您可以通过自动布局内容拥抱/压缩阻力优先级或直接在手动布局期间使用该值来利用固有内容高度。

\n

不幸的是,这种行为没有记录在案。Apple 可以轻松地为我们省去一些麻烦\xe2\x80\xa6,无需额外的高度限制、子类化等。

\n


ma1*_*w28 7

您也可以在没有子类化的情况下完成UITextView.看看我如何根据iOS 7的内容调整UITextView的大小?

使用此表达式的值:

[textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height
Run Code Online (Sandbox Code Playgroud)

更新constant了的textView的高度UILayoutConstraint.

  • 你在做什么,你还在做什么,以确保UITextView全部设置好?我这样做,文本视图本身大小,但文本本身在UITextView的高度中间被切断.在执行上述操作之前,会以编程方式设置文本. (3认同)