Expanding Uitextview Inside a Stack View with Scrolling Enabled

Expanding UITextView inside a Stack View with scrolling enabled

Couple notes...

I think you'll be better off setting the Max Height of your UITextView rather than of your UIStackView. The Text View will expand / contract based on its content, so you can set a <= height constraint.

Calculate the "elementsHeight" - the vertical size used by your top and bottom views, plus any spacing - and set the "max text view height" to the view height minus elementsHeight ... and subtract keyboard height when visible.

Update the text view's height constraint constant when "max text view height" changes.

Then set up an Observer for the text view's .contentSize ... and enable / disable scrolling based on .contentSize.height compared to "max text view height".

Needs a bit of hoop-jumping, to make sure you update sizes when subviews are laid-out, and to make sure you don't get in a recursion loop.

This is how I set it up:

Sample Image

The initial height constraint on the text view is <= 40 - but that doesn't really matter, as it will be changed via code every time the views layout differently.

I put up an example on GitHub -- it works, but is really just a starting-point. Take a look if you're interested, and pick it apart. https://github.com/DonMag/ExpandingTextView

Expand UITextView and then scroll after certain point

It's better to make a default height constraint and connect it's outlet and play with it's constant

self.txH.constant = newHeight
self.view.layoutIfNeeded()

Autolayout issue with dynamically sized, scrolling-enabled UITextView

There is, unfortunately, no "auto" method of enabling/disabling UITextView scrolling in the manner you want.

One option is to determine the max-height for your text view, and then use an observer to watch for changes in the text view's contentSize. When the contentSize height exceeds your max-height, enable scrolling. See this answer for additional details (along with a link to an example project): Expanding UITextView inside a Stack View with scrolling enabled

Another option is to embed a non-scrolling UITextView in a scrollable UIScrollView. The trick is to constrain the scrollview's height to the height of the text view, but give it a Priority of 250. Setup all other constraints as normal. The scrollview's height will expand to match the text view's height as it changes, but the other constraints will restrict it so it doesn't keep growing.

How do I get UITextView field to expand when adding text and scrolling is disabled?

Everyone was very diligent about trying to help me resolve this issue. I tried each one and was not able to implement any of them with satisfactory results.

I was directed to this solution by an associate: https://stackoverflow.com/a/36070002/152205 and with the following modifications was able to solve my problem.

    // MARK: UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
let startHeight = textView.frame.size.height
let calcHeight = textView.sizeThatFits(textView.frame.size).height

if startHeight != calcHeight {

UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
self.tableView.endUpdates()

// let scrollTo = self.tableView.contentSize.height - self.tableView.frame.size.height
// self.tableView.setContentOffset(CGPoint(x: 0, y: scrollTo), animated: false)

UIView.setAnimationsEnabled(true)
}
}

Note: The scrollTo option caused the content to shift up several cell. With that removed everything worked as expected.

UITextView inside UIScrollView with AutoLayout

After a few days of research and getting my hands dirty with UIScrollView + UITextView + Auto Layout, I successfully got a fully working UIScrollView. I want to share my solution just in case someone might stuck on the same situation.

  1. Add UIScrollView inside the main view in Storyboard
  2. Add UIView inside the UIScrollView
  3. Add UITextView inside the UIView (the view added in step 2)
  4. Make sure "Scrolling Enabled" of UITextView is unchecked
  5. Add 4 constraints (leading, trailing, top, bottom) on UIScrollView
  6. Add 4 constraints (leading, trailing, top, bottom) on UIView (the view added in step 2)
  7. Add "Width Equally" constraint on UIView (the view added in step 2) and the main view
  8. Add 5 constraints (leading, trailing, top, bottom, height) on UITextView. After this step you shouldn't get any errors and warnings on constraints.
  9. Add UITextView height constraint IBOutlet on the ViewController. @property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewHeightConstraint; and connect it in Storyboard
  10. Change the UITextView height constraint programmatically. self.textViewHeightConstraint.constant = [self.textView sizeThatFits:CGSizeMake(self.textView.frame.size.width, CGFLOAT_MAX)].height;

After all of these 10 steps, you'll get fully working UIScrollView with UITextView inside and be happy.

UITextView that expands to text using auto layout

The view containing UITextView will be assigned its size with setBounds by AutoLayout. So, this is what I did. The superview is initially set up all the other constraints as they should be, and in the end I put one special constraint for UITextView's height, and I saved it in an instance variable.

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

[self addConstraint:_descriptionHeightConstraint];

In the setBounds method, I then changed the value of the constant.

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

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

[_descriptionHeightConstraint setConstant:descriptionSize.height];

[self layoutIfNeeded];
}

Toggle Enabling UITextView Scroll After Max Number of Lines

You have overcomplicated a simple problem at hand, here is how can you achieve what you want

class ViewController: UIViewController {
var heightConstraint: NSLayoutConstraint!
var heightFor5Lines: CGFloat = 0

override func viewDidLoad() {
super.viewDidLoad()
let textView = UITextView(frame: CGRect.zero)
textView.delegate = self
textView.backgroundColor = UIColor.red
textView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(textView)
textView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
textView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
heightConstraint = textView.heightAnchor.constraint(equalToConstant: 30.0)
heightConstraint.isActive = true
}
}

extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let numberOfLines = textView.contentSize.height/(textView.font?.lineHeight)!

if Int(numberOfLines) > 5 {
self.heightConstraint.constant = heightFor5Lines
} else {
if Int(numberOfLines) == 5 {
self.heightFor5Lines = textView.contentSize.height
}
self.heightConstraint.constant = textView.contentSize.height
}
textView.layoutIfNeeded()
}
}

Sample Image

How this works?:

Its simple, your textView starts off with some height (height constraint is necessary here because I haven't neither disabled its scroll nor have I provided bottom constraint, so it does not have enough data to evaluate its intrinsic height) and every time text changes you check for number of lines, as long as number of lines is less than threshold number of lines you keep increasing the height constraint of your textView so that its frame matches the contentSize (hence no scrolling) and once it hits the expected number of lines, you restrict height, so that textView frame is less than the actual content size, hence scrolls automatically

Hope this helps



Related Topics



Leave a reply



Submit