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
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
this Works for me, all other solutions didn't.
func adjustUITextViewHeight(arg : UITextView) {
arg.translatesAutoresizingMaskIntoConstraints = true
arg.sizeToFit()
arg.scrollEnabled = false
}
In Swift 4 the syntax of arg.scrollEnabled = false
has changed to arg.isScrollEnabled = false
.
In Storyboard / Interface Builder simply disable scrolling in the Attribute inspector.
In code textField.scrollEnabled = false
should do the trick.
All I had to do was:
This allows autolayout to dynamically size the textView based on its content.
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
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
}
Followed by DeyaEldeen's answer.
In my case. I grow the textview height automatically by adding
swift 3
textView.translatesAutoresizingMaskIntoConstraints = false
textView.isScrollEnabled = false
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)
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
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.
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()
}
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
it's straight forward to do in programatic way. just follow these steps
add an observer to content length of textfield
[yourTextViewObject addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
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];
}];
}
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;
}
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
.
textView
to the contentView
with constraints.textView
.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.
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)
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
}
}
Better yet swift 4 add as an extension:
extension UITextView {
func resizeForHeight(){
self.translatesAutoresizingMaskIntoConstraints = true
self.sizeToFit()
self.isScrollEnabled = false
}
}
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
}
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();
});
}
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
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),
])
}
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.
if you want preload textView
on controller load. Call this function from view didload()
:
func textViewDidChange(_ textView: UITextView) {
let esmitated = CGSize(width: txtView.frame.width, height: .infinity)
let esmitatedSize = txtView.sizeThatFits(esmitated)
self.heghtConstraint.constant = esmitatedSize.height
}