Resize textField Based On Content - ios

How can a textField be resized based on content while using auto-layout in an iOS application written in Swift?
The text field will resize as necessary to fit its content when the view loads as well as while the user is typing.
Ideally, the text field would stop resizing at a certain point, say, 6 lines, and become scrollable.

You have to use an UITextView instead of an UITextField.
Then, you can use the sizeThatFits method.
But first you have to know how high one line will be. You can get that information by using lineHeight:
var amountOfLinesToBeShown: CGFloat = 6
var maxHeight: CGFloat = yourTextview.font.lineHeight * amountOfLinesToBeShown
After that, just call the sizeThatFits method inside your viewDidLoad method and set the maxHeight (line * 6) as your textview height:
yourTextview.sizeThatFits(CGSizeMake(yourTextview.frame.size.width, maxHeight))

Swift 3
var textView : UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView = UITextView()
textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: textView.frame.size.height))
}

Related

Dynamic UITextView Size (Swift 4)

I want size a UITextView dynamically to its contents by restricting both its width and its height to some maximums.
In particular, the textView should have a minimum width and height if there is little data (e.g., "Lorem"). As the data grows (e.g., "Lorem ipsum") it should expand in width as until it hits the maximum width ("Lorem ipsum dolor sit"), and then expand in height until it hits that maximum height. Once it goes beyond the max width and height it should become scrollable.
So far, I am struggling to get just the height working. I have experimented with a number of methods and, while this seemed the simplest and most promising, it just crashes.
func textViewDidChange(_ textView: UITextView ) {
resizeTextView(txtview1)
}
#IBOutlet weak var txtViewHeight: NSLayoutConstraint!
func resizeTextView(_ textView: UITextView ) {
let minHeight: CGFloat = 20.0
let maxHeight: CGFloat = 40.0
txtViewHeight.constant = min(maxHeight, max(minHeight, textView.contentSize.height))
self.view.layoutIfNeeded()
}
So, I changed the resize function to this - which kinda, sorta, sometimes works (sometimes the values show up in the text field, sometimes it doesn't display at all, sometimes the spacing within the text field is different):
func resizeTextView(_ textView: UITextView ) {
let currentHeight = textView.contentSize.height
let minHeight: CGFloat = 20.0
let maxHeight: CGFloat = 40.0
if currentHeight > maxHeight {
textView.frame.size.height = currentHeight
textView.isScrollEnabled = true
}
else if currentHeight < minHeight {
textView.frame.size.height = minHeight
textView.isScrollEnabled = false
}
else {
textView.frame.size.height = currentHeight
textView.isScrollEnabled = false
}
self.view.layoutIfNeeded()
}
I know this question has been asked an answered a dozen or more times, but none of the solutions are working for me (the above being two of the half dozen or more I have tried).
I think it is better to achieve this with Timer.
You solution above is that, UITextView's delegate methods triggers your height changing method.
Your changing method affects the UITextView's delegate method under the hood.
In other words, it is weird that you observe UITextView's property, then do something and affect the UITextView's property,
then the observation triggers again.
to achieve this with Timer, then the height changing events is from your timer source, not from Apple's UIKit
start editing, timer run
end editing, timer gone

Swift UIScrollView height dependent upon UITextView content

I have a UIScrollView with multiple views inside of it, one of them being a UITextView. I'm trying to set the height of scroll view to match the content. The problem is that the UITextView has a height that varies according to the text it contains. This content is set in the override viewDidLoad() method in the view controller, but for some reason the value I get for the height of the view does not reflect any changes, even after the value of the content has changed. I have a function written to change the height of the content subview by changing it's height constraint constant, which I am calling in the viewDidAppear() method. Relevant code is as follows:
func setPageSize() {
var pageHeight : CGFloat {
var height : CGFloat = 1000 // The height of all content besides the UITextView
let descriptionTextViewHeight = self.descriptionTextView.frame.height
height += descriptionTextViewHeight
return height // Always returns 60, which is the height of view when using the placeholder text from interface builder (doesn't update when text updated).
}
self.pageViewHeightConstraint.constant = pageHeight
}
override func viewDidLoad() {
super.viewDidLoad()
self.descriptionTextView.text = self.description
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.setPageSize()
}
Should I call the setPageSize() method somewhere else? Is there a method in UITextView that returns an updated value when new text is entered? I have also tried using the location of the last view in the content view for reference in setting the height, but I'm having the same problem - it's returning a value as if the UITextView's height is always 60, which it's not.
Using
let descriptionTextViewHeight = self.descriptionTextView.frame.height
returns visible height of the textView,
To get height of the scrollable content inside the textView , you can use
let descriptionTextViewHeight = self.descriptionTextView.contentSize.height
OK. I'm not certain why this works, but if I put this line that sets the value of the text:
self.descriptionTextView.text = self.description
in viewDidLayoutSubviews() instead of viewDidLoad() it works. I'm not sure why that is, since viewDidLoad() is called before viewDidLayoutSubviews(), and both are called before viewDidAppear(); but for some reason this returns the proper value of the UITextView's height with:
self.descriptionTextView.frame.height
in the viewDidAppear() method.

iOS TextField view glides out of layout

I'm using TextField in my app. I made it to wrap the content.
The problem is when the user types a long text the TextField edges glides out of the layout and make some of the view invisible.
is there a way to disable it to expend when it reaches to the layout edges?
The best thing to do is to change your UITextField to a UITextView. Here's a function that I like to use quite a lot for this autoresize technique that you'll see in the likes of Apples iMessage:
func containerViewHeight() {
let size = textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
containerView.heightAnchor.constraint(equalToConstant: size.height + 24)
self.textView.setContentOffset(.zero, animated: false)
}
You'll want to call this function initially inside of your viewDidLoad:
override func viewDidLoad() {
self.containerViewHeight()
}
As well as that, you'll want to conform to the UITextViewDelegate methods by subclassing it at the top of your file like so:
class YourViewController: UIViewController, UITextViewDelegate
Once you add this AND you have used self.textView.delegate = self inside of your viewDidLoad:
override func viewDidLoad() {
self.textView.delegate = self
}
you'll then be able to use the textViewDidChange method for that textView, so the final thing you'll want to add in your class is this:
func textViewDidChange(_ textView: UITextView) {
self.containerViewHeight()
}
Your textField probably doesn't have a fixed width.
Just put a width constraint to your textField in your storyboard so it will always have the same width, no matter if the text is too long.
Edit : if you want a maximum width, you can add 2 width constraints to your textField. One for minimum width and one for maximum width. This way the width of your textField will vary between 100 and 200, depending on the text it contains.
Minimum width constraint :
Maximum width constraint :

Animate automatic UILabel text size change [duplicate]

This question already has answers here:
Animating UILabel Font Size Change
(9 answers)
Closed 6 years ago.
I have set my UILabel to scale text automatically to fit within the labels frame. During runtime I am changing the size of this frame to make space for things underneath, but I really want the size change to be animated, since the text jumps from one size to a smaller one, which doesn't look very good.
Is there any way to accomplish this?
To animate the height change use UIView.animateWithDuration. Here's an example animating the height change of a UILabel with a 2 second duration:
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let updatedHeight: CGFloat = 100.0
let updatedFrame = CGRect(origin:label.frame.origin,
size: CGSize(width:label.frame.width, height: updatedHeight))
UIView.animateWithDuration(2.0, animations: {
self.label.frame = updatedFrame
})
}

ContentOffset doesn't do anything to UITextView the first time

I'm trying to have a UIViewController to display some text centered vertically in a UITextView. When app is launched, the text is aligned to the top. But if I try to select some text, the text would then move to the center vertically. It seems like setting contentOffset doesn't do anything at the first time, unless there's some action to trigger it to readjust the layout (like selecting some text in my scenario).
Also when I log the values of height, size, contentHeight and topCorrect, the values from the first call is the same as the second call. So I'm getting the same value but UITextView somehow is not responding to the contentOffset the very first time.
I even tried moving the logic to viewDidLayoutSubviews() and viewDidAppear(). I had the same result. So I don't think it's a problem of having the logic in the wrong place.
I think some people have success with it on iOS 7 and Obj-C. I'm trying get it running on iOS 8 and Swift.
Any suggestion or help would be very much appreciated.
class PageContentViewController : UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.textView.textContainerInset = UIEdgeInsetsMake(0, 16.0, 0, 16.0)
let height = self.textView.bounds.size.height
let size = CGSizeMake(self.textView.frame.width, CGFloat.max)
var contentHeight = self.textView.sizeThatFits(size).height
var topCorrect = (height - contentHeight * self.textView.zoomScale) / 2.0
topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
self.textView.setContentOffset(CGPointMake(0, -topCorrect), animated: false)
}
}

Resources