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 :
Related
I'm trying to use a custom view as an accessory view over the keyboard, for various reasons, in this case, it is much preferred over manual keyboard aligning because of some other features.
Unfortunately, this is a dynamic view that defines its own height. The constraints all work fine outside of the context of an accessoryView without errors, and properly resizing
When added as a keyboardAccessoryView it seems to impose a height of whatever the frame is at the time and break other height constraints
It appears as:
"<NSLayoutConstraint:0x600003e682d0 '_UIKBAutolayoutHeightConstraint' Turntable.ChatInput:0x7fb629c15050.height == 0 (active)>"
(where 0 would correspond to whatever height had been used at initialization
It is also labeled accessoryHeight which should make it easy to remove, but unfortunately, before I can do this, I'm getting unsatisfiable constraints and the system is tossing my height constraints
Tried:
in the inputAccessoryView override, I tried to check for the constraints and remove it, but it doesn't exist at this time
setting translatesAutoresizing...Constraints = false
tl;dr
Using a view as a KeyboardAccessoryView is adding its own height constraint after the fact, can I remove this?
Looks like keyboard doesn't like inputAccessoryView with height constraint. However you still can have inputAccessoryView with dynamic height by using frame (it is still possible to use constraints inside your custom inputAccessoryView).
Please check this example:
import UIKit
final class ViewController: UIViewController {
private let textField: UITextField = {
let view = UITextField()
view.frame = .init(x: 100, y: 100, width: 200, height: 40)
view.borderStyle = .line
return view
}()
private let customView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.frame.size.height = 100
view.autoresizingMask = .flexibleHeight // without this line height won't change
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textField)
textField.inputAccessoryView = customView
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.customView.frame.size.height = 50
self.textField.reloadInputViews()
}
}
}
The placeholder text does show the triple points to cut off the text.
The text doesn't even take half of the textfield width, and the three points are longer than the text. Is there a way to go inside the field and adjust the threshold to avoid the cutoff?
I do know the programatic way, and use that for the moment, to resize the placeholder tekst, till it fits.
let labelKeyPath: String = "_placeholderLabel"
var label : UILabel = self.Distance.value(forKeyPath: labelKeyPath) as! UILabel
label.adjustsFontSizeToFitWidth = true
label = self.FocalLength.value(forKeyPath: labelKeyPath) as! UILabel
label.adjustsFontSizeToFitWidth = true
Herby I do add some images of the current IB settings and results. Please keep adding points to let me show the current and future images embedded, thank you.
The IB view, settings and app result
Alternative you can create your own UITF subclass and override:
open func placeholderRect(forBounds bounds: CGRect) -> CGRect
You can customize the textfield to fix you issue. put this CustomTextField in your Storyboard textfield class type. here am setting minimum scaling factor to 0.3. that helps to fix the ...
class CustomTextField: UITextField {
override func layoutSubviews() {
super.layoutSubviews()
for subview in subviews {
if let label = subview as? UILabel {
label.minimumScaleFactor = 0.3
label.adjustsFontSizeToFitWidth = true
}
}
}
}
You should change Autolayouts Constraints with leading, center vertically with Distance and horizontal space with the Distance.
You can also wrap up text with resize the textfield using minimum font size.
I am using a UITextView inside a tableView cell to hold varying sized text content with scrolling disabled.
In order to auto-size the UITextView I've used auto-layout to pin it to the layout and also added this method to adjust the height:
override func viewWillAppear(_ animated: Bool) {
tableView.estimatedRowHeight = 50
tableView.rowHeight = UITableViewAutomaticDimension
}
This works correctly on the initial view - when the content first loads. However, I also want the user to be able to edit the text when they tap into the content (similar to the Apple Reminders app). This works correctly with one limitation: UITextView does not expand as the content grows.
How do I enable UITextView to expand during editing without scrolling?
New details:
Here is a screenshot of the current settings.
Per Matt's recommendations below, I created the following subclass.
class MyTextView: UITextView {
#IBOutlet var heightConstraint : NSLayoutConstraint?
override func awakeFromNib() {
super.awakeFromNib()
self.heightConstraint?.isActive = false
}
}
I had to modify the forced unwrapping to avoid a fatal error.
How do I enable UITextView to expand during editing without scrolling
A self-sizing text view is very simple; for a non-scrolling text view with no height constraint, it's the default. In this example, I've added some code to remove the existing height constraint, though you could do that in the storyboard just by indicating that the height constraint is a placeholder:
class MyTextView : UITextView {
#IBOutlet var heightConstraint : NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
self.heightConstraint.isActive = false
}
}
Screencast of the result:
If you subsequently do a batch update on the table view, and assuming the cell's other internal constraints are right, the cell will be remeasured as well (but I didn't demonstrate that as part of the example).
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.
you could use var sizeThatFits
override func viewDidLoad() {
super.viewDidLoad()
textView = UITextView()
textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: textView.frame.size.height))
}
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.
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))
}