'how to make UITextView height dynamic according to text length?

As you can see in this image

the UITextView changes it's height according to the text length, I want to make it adjust it's height according to the text length.

*I saw other questions, but solutions there didn't work for me

example



Solution 1:[1]

In Storyboard / Interface Builder simply disable scrolling in the Attribute inspector.

In code textField.scrollEnabled = false should do the trick.

Solution 2:[2]

All I had to do was:

  1. Set the constraints to the top, left, and right of the textView.
  2. Disable scrolling in Storyboard.

This allows autolayout to dynamically size the textView based on its content.

Solution 3:[3]

Give this a try:

CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;
self.textView.frame = frame;

Edit- Here's the Swift:

var frame = self.textView.frame
frame.size.height = self.textView.contentSize.height
self.textView.frame = frame

Solution 4:[4]

Swift 4

Add It To Your Class

UITextViewDelegate

func textViewDidChange(_ textView: UITextView) {
      let fixedWidth = textView.frame.size.width
      textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
      let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
      var newFrame = textView.frame
      newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
      textView.frame = newFrame
}

Solution 5:[5]

Followed by DeyaEldeen's answer.
In my case. I grow the textview height automatically by adding

swift 3

textView.translatesAutoresizingMaskIntoConstraints = false textView.isScrollEnabled = false

Solution 6:[6]

Swift 5, Use extension:

extension UITextView {
    func adjustUITextViewHeight() {
        self.translatesAutoresizingMaskIntoConstraints = true
        self.sizeToFit()
        self.isScrollEnabled = false
    }
}

Usecase:

textView.adjustUITextViewHeight()

And don't care about the height of texeView in the storyboard (just use a constant at first)

Solution 7:[7]

just make a connection with your textView's height Constraint

@IBOutlet var textView: UITextView!
@IBOutlet var textViewHeightConstraint: NSLayoutConstraint!

and use this code below

textViewHeightConstraint.constant = self.textView.contentSize.height

Solution 8:[8]

If your textView is allowed to grow as tall as the content, then

textView.isScrollEnabled = false

should just work with autolayout.

If you want to remain the textView to be scrollable, you need to add an optional height constraint,

internal lazy var textViewHeightConstraint: NSLayoutConstraint = {
  let constraint = self.textView.heightAnchor.constraint(equalToConstant: 0)
  constraint.priority = .defaultHigh
  return constraint
}()

public override func layoutSubviews() {
  super.layoutSubviews()

  // Assuming there is width constraint setup on the textView.
  let targetSize = CGSize(width: textView.frame.width, height: CGFloat(MAXFLOAT))
  textViewHeightConstraint.constant = textView.sizeThatFits(targetSize).height
}

The reason to override layoutSubviews() is to make sure the textView is laid out properly horizontally so we can rely on the width to calculate the height.

Since the height constraint is set to a lower priority, if it runs out space vertically the actual height of the textView will be less than the contentSize. And the textView will be scrollable.

Solution 9:[9]

I added these two lines of code and work fine for me.

Works in Swift 5+

func adjustUITextViewHeight(textView : UITextView)
{
     textView.translatesAutoresizingMaskIntoConstraints = true
     textView.isScrollEnabled = false
     textView.sizeToFit()
}

Solution 10:[10]

This answer may be late but I hope it helps someone.

For me, these 2 lines of code worked:

textView.isScrollEnabled = false
textView.sizeToFit()

But don't set height constraint for your Textview

Solution 11:[11]

Swift 4+

This is extremely easy with autolayout! I'll explain the most simple use case. Let's say there is only a UITextView in your UITableViewCell.

  • Fit the textView to the contentView with constraints.
  • Disable scrolling for the textView.
  • Update the tableView on textViewDidChange.

That's all!

protocol TextViewUpdateProtocol {
    func textViewChanged()
}

class TextViewCell: UITableViewCell {
    
    //MARK: Reuse ID
    static let identifier = debugDescription()
    
    //MARK: UI Element(s)
    /// Reference of the parent table view so that it can be updated
    var textViewUpdateDelegate: TextViewUpdateProtocol!
    
    lazy var textView: UITextView = {
        let textView = UITextView()
        textView.isScrollEnabled = false
        textView.delegate = self
        textView.layer.borderColor = UIColor.lightGray.cgColor
        textView.layer.borderWidth = 1
        textView.translatesAutoresizingMaskIntoConstraints = false
        return textView
    }()
    
    //MARK: Padding Variable(s)
    let padding: CGFloat = 50
    
    //MARK: Initializer(s)
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        addSubviews()
        addConstraints()
        
        textView.becomeFirstResponder()
    }
    
    //MARK: Helper Method(s)
    func addSubviews() {
        contentView.addSubview(textView)
    }
    
    func addConstraints() {
        textView.leadingAnchor  .constraint(equalTo: contentView.leadingAnchor, constant: padding).isActive = true
        textView.trailingAnchor .constraint(equalTo: contentView.trailingAnchor, constant: -padding).isActive = true
        textView.topAnchor      .constraint(equalTo: contentView.topAnchor, constant: padding).isActive = true
        textView.bottomAnchor   .constraint(equalTo: contentView.bottomAnchor, constant: -padding).isActive = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

extension TextViewCell: UITextViewDelegate {
    
    func textViewDidChange(_ textView: UITextView) {
        textViewUpdateDelegate.textViewChanged()
    }
    
}

Now you have to inherit implement the protocol in your ViewController.

extension ViewController: TextViewUpdateProtocol {
    
    func textViewChanged() {
        tableView.beginUpdates()
        tableView.endUpdates()
    }
    
}

Check out my repo for the full implementation.

Solution 12:[12]

SWIFT 4

Change the size when typing

UITextViewDelegate

func textViewDidChange(_ textView: UITextView) {
        yourTextView.translatesAutoresizingMaskIntoConstraints = true
        yourTextView.sizeToFit()
        yourTextView.isScrollEnabled = false

        let calHeight = yourTextView.frame.size.height
        yourTextView.frame = CGRect(x: 16, y: 193, width: self.view.frame.size.width - 32, height: calHeight)
    }

Change the size when load

func textViewNotasChange(arg : UITextView) {
        arg.translatesAutoresizingMaskIntoConstraints = true
        arg.sizeToFit()
        arg.isScrollEnabled = false

        let calHeight = arg.frame.size.height
        arg.frame = CGRect(x: 16, y: 40, width: self.view.frame.size.width - 32, height: calHeight)
    }

Call the function of the second option like this:

textViewNotasChange(arg: yourTextView)

Solution 13:[13]

it's straight forward to do in programatic way. just follow these steps

  1. add an observer to content length of textfield

    [yourTextViewObject addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
    
  2. implement observer

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    UITextView *tv = object;
    
        //Center vertical alignment
        CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
        topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
        tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
    
    
        mTextViewHeightConstraint.constant = tv.contentSize.height;
    
        [UIView animateWithDuration:0.2 animations:^{
    
            [self.view layoutIfNeeded];
        }];
    
    }
    
  3. if you want to stop textviewHeight to increase after some time during typing then implement this and set textview delegate to self.

    -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
    {
        if(range.length + range.location > textView.text.length)
        {
            return NO;
        }
    
        NSUInteger newLength = [textView.text length] + [text length] - range.length;
    
        return (newLength > 100) ? NO : YES;
    
    }
    

Solution 14:[14]

In my project, the view controller is involved with lots of Constraints and StackView, and I set the TextView height as a constraint, and it varies based on the textView.contentSize.height value.

step1: get a IB outlet

@IBOutlet weak var textViewHeight: NSLayoutConstraint!

step2: use the delegation method below.

extension NewPostViewController: UITextViewDelegate {
     func textViewDidChange(_ textView: UITextView) {
          textViewHeight.constant = self.textView.contentSize.height + 10
     }
}

Solution 15:[15]

Better yet swift 4 add as an extension:

extension UITextView {
    func resizeForHeight(){
        self.translatesAutoresizingMaskIntoConstraints = true
        self.sizeToFit()
        self.isScrollEnabled = false
    }
}

Solution 16:[16]

its working

func textViewDidChange(_ textView: UITextView) {
    let fixedWidth = textviewconclusion.frame.size.width
    textviewconclusion.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
    let newSize = textviewconclusion.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
    var newFrame = textviewconclusion.frame
    newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
    textviewconclusion.frame = newFrame
}

Solution 17:[17]

1 Add an observer to the content length of textfield

   yourTextView.addObserver(self, forKeyPath: "contentSize", options: (NSKeyValueObservingOptions.new), context: nil);

2 Implement observer

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        let tv = object as! UITextView;
        var topCorrect = (tv.bounds.size.height - tv.contentSize.height * tv.zoomScale)/2.0;
        topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
        tv.contentOffset.x = 0;
        tv.contentOffset.y = -topCorrect;
        self.yourTextView.contentSize.height = tv.contentSize.height;
        UIView.animate(withDuration: 0.2, animations: {
            self.view.layoutIfNeeded();
        });
    }

Solution 18:[18]

Here are two pitfalls in iOS 8.3 when coming with textView.textContainer.maximumNumberOfLines = 10

Refer to my gist, please.

textView.attributedText = originalContent

let lineLimit = 10
textView.isEditable = true
textView.isScrollEnabled = false
textView.textContainerInset = .zero // default is (8, 0, 8, 0)
textView.textContainer.maximumNumberOfLines = lineLimit // Important condition
textView.textContainer.lineBreakMode = .byTruncatingTail

// two incomplete methods, which do NOT work in iOS 8.3

// size.width???maxSize.width? ————???? iOS 8.3 ??????maximumNumberOfLines?????????UILabel
// size.width may be less than maxSize.width, ---- Do NOT work in iOS 8.3, which disregards textView.textContainer.maximumNumberOfLines
// let size = textView.sizeThatFits(maxSize) 

// ???? iOS 8.3 ????????????UILabel
// Does not work in iOS 8.3
// let size = textView.layoutManager.usedRectForTextContainer(textView.textContainer).size 

// Suggested method: use a temperary label to get its size
let label = UILabel(); label.attributedText = originalContent
let size = label.textRect(forBounds: CGRect(origin: .zero, size: maxSize), limitedToNumberOfLines: lineLimit).size
textView.frame.size = size

Solution 19:[19]

Declaration here

    fileprivate weak var textView: UITextView!

Call your setupview here

    override func viewDidLoad() {
        super.viewDidLoad()

         setupViews()
    }

Setup here

    fileprivate func setupViews() {

    let textView = UITextView()
    textView.translatesAutoresizingMaskIntoConstraints = false
    textView.text = "your text here"
    textView.font = UIFont.poppinsMedium(size: 14)
    textView.textColor = UIColor.brownishGrey
    textView.textAlignment = .left
    textView.isEditable = false
    textView.isScrollEnabled = false
    textView.textContainerInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

   self.view.addSubview(textView)

   self.textView = textView

   setupConstraints()

   }

Setup constraints here

   fileprivate func setupConstraints() {

    NSLayoutConstraint.activate([

        textView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
        textView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20),
        textView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20),
        textView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20),
        textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 150),
        ])
  }

Solution 20:[20]

Whenever you need to resize the textview according to the inside content size, like in messageing app. Use cocoapods(GrowingTextView), it will make your life easier, than coding the dynamic resizing of textview on your own.

Solution 21:[21]

  1. Put textView in StackView
  2. Set constraints(top, bottom, left, right) for stackView
  3. Add height constraint to StackView, select this constraint and set it 'Greater Than or Equal' in Relation, on the right panel