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:
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.
- Add UIScrollView inside the main view in Storyboard
- Add UIView inside the UIScrollView
- Add UITextView inside the UIView (the view added in step 2)
- Make sure "Scrolling Enabled" of UITextView is unchecked
- Add 4 constraints (leading, trailing, top, bottom) on UIScrollView
- Add 4 constraints (leading, trailing, top, bottom) on UIView (the view added in step 2)
- Add "Width Equally" constraint on UIView (the view added in step 2) and the main view
- Add 5 constraints (leading, trailing, top, bottom, height) on UITextView. After this step you shouldn't get any errors and warnings on constraints.
- Add UITextView height constraint IBOutlet on the ViewController.
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewHeightConstraint;
and connect it in Storyboard - 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()
}
}
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
How to Compare Two Strings to Check If They Have Same Characters Swift 4
How to Get All Text from a PDF in Swift
Custom Cell with Uitableview Inside Uicollectionviewcell
Simple Swift Class Does Not Compile
How to Make Exponents in the Swiftui
How to Retrieve Audio File from Parse Swift
Swiftui Mysterious Spacing Between Large Text and Textfield in VStack
Equivalent of Skaction Scaletox for a Given Duration in Unity
Why Do We Need to Specify Init Method
Switch Statement for Imported Ns_Options (Rawoptionsettype) in Swift
Accessing Struct from One Class to Another
Swift Access Array with Index Gives Following Error. Any Idea Why
Auth.Auth().Currentuser.Reload() Doesn't Refresh Currentuser.Isemailverified
Check If an Int Value Is Greater or Equal to Another Int? Value
How to Reset a Subview in Swiftui
How to Display Mtkview with Rgba16Float Mtlpixelformat
Make Swiftui Rectangle Same Height or Width as Another Rectangle