I am facing an unexpected behavior with my UIlabel sizeToFit() method. Now I have tried making the numberOfLines = 0, I event called LayoutIfNeed(). But None of them works.
I even tried methods given in this question : Vertically align text to top within a UILabel
But Again None helped. I don't have to many constraints, I am just using Auto resizing pins. I even tried it with no constraints or no auto resize.
I have this label set up in TableViewCell and CollectionReusableView. Calling it in awakeFromNib() just doesnt affect.
UIcollectionReusableView code :
override func awakeFromNib() {
super.awakeFromNib()
label.sizeToFit()
label.numberOfLines = 0
label.layer.shadowOffset = CGSize(width: 0, height: 0)
label.layer.shadowOpacity = 3
label.layer.shadowRadius = 8
}
constrains :
TableViewCell Code:
override func layoutSubviews() {
super.layoutSubviews()
// this is the UIview on which the label is put on.
contentView.layoutIfNeeded()
}
override func awakeFromNib() {
super.awakeFromNib()
// This is the label has the issue
caption.sizeToFit()
}
Constraints :
Also, Label in the text view is stacked with Another Label and stackview constrainst are :
here is the example:
It Just doesn't seem to work. I am totally out of idea.
Any help is greatly appreciated.
You need to call sizeToFit() after the text is added to the label, not before.
Related
I have custom tableViewCell. And in it cell i have custom separator. The logic of work should be like this:
if select textField in cell - separator change color and
height(from 1 to 2)
if type text - separator color and height not
change
Now it's work like:
if not added constrain for height for separator in .xib - added constrain for height when create cell, but in screen it equal 0
override func awakeFromNib() {
customSeparator.backgroundColor = .lightGray
customSeparator.frame.size.height = 1.0
}
if added constrain for height for separator in .xib - when select cell height of separator change(like expected). But when type text height change to value specified in .xib
func textFieldDidBeginEditing(_ textField: UITextField) {
customSeparator.backgroundColor = .black
customSeparator.frame.size.height = 2.0
}
So, why is this happening, tell me, plz
You need to create an height IBOutlet constraint and change it as follows:
#IBOutlet weak var customSeparatorHeight: NSLayoutConstraint!
override func awakeFromNib() {
customSeparator.backgroundColor = .lightGray
customSeparatorHeight.constant = 1
self.contentView.layoutIfNeeded()
}
Labels are really convenient in iOS autolayout because they don't require a constraints to determine size, they only require an X and a Y position. For static text this is great.
I have a custom view that I would like to provide a default size to auto layout similar to UILabels. Is this possible? I am familiar with IBInspectable and IBDesignable, but I'm not sure how to implement this. I am using autolayout in storyboards, but I imagine the solution would work for storyboards + programmatic.
I know I can just set the height and width, but this is view that will be used everywhere so it would be nice to have the width / height dynamic.
Unfortunately, there is no smooth way to add UIView without specifying its size values, i.e. its width and height.
Speaking of auto-layout, there are 2 options you got, probably you are already aware of too. 1) you should either set UIView's size values, or 2. you set other UI Objects' sizes so that auto-layout can understand the size of UIView.
Speaking of programmatically, upon creating UIView object, if you do not provide frame, then it is not shown. Although this might be a solution to put at any point with any size, it might be not ideal when you are using xibs or storyboards, since there would be a gap on those interfaces which may also confuse development.
The way I am thinking of includes using both intrinsicContentSize and IBDesignable. I made a little demo for this purpose and you can find this code below. I will share 2 examples, one is inherited from UILabel and the other is inherited from UIView, so that I can show usage of intrinsicContentSize easier.
Below is the UIView one.
import UIKit
#IBDesignable class UIDemoView: UIView {
func setup() {
layer.cornerRadius = frame.height / 5
clipsToBounds = true
}
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override var intrinsicContentSize: CGSize {
let newWidth = 100
let newHeight = 100
let newSize = CGSize(width: newWidth, height: newHeight)
return newSize
}
}
And below is the UILabel one.
import UIKit
#IBDesignable class UIDemoLabel: UILabel {
func setup() {
layer.cornerRadius = frame.height / 2
clipsToBounds = true
textAlignment = .center
}
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override var intrinsicContentSize: CGSize {
let superSize = super.intrinsicContentSize
let newWidth = superSize.width + superSize.height
let newHeight = superSize.height
let newSize = CGSize(width: newWidth, height: newHeight)
return newSize
}
}
Here is the output on auto-layout
I intentionally showed the constraints on both object, so you can check if I understand you correctly. They both have only 2 constraints: top and center alignment.
UILabels have theirs own intrinsicContentSize, so when overriding them we might use their already given intrinsicContentSize as a reference, as a guidance to whatever we want to do. UIViews intrinsicContentSize is not set, therefore we must find a way to give them specific sizes. With not being sure, I am guessing for UILabel intrinsicContentSize implementation, logic is most probably related with the size of the text. You can simply pass constants like above code, or you can use any custom logic to provide CGFloats.
Just a thought of mine: Even though I came with this solution, I am not fan of IBDesignables. From my perspective, they slow down the development because of its buggy nature. Thus, to be fully honest, I'd rather 1. putting UIView objects on xibs and storyboards, 2. changing its class from UIView to custom UIView class, 3. settings its constraints, 4. setting remaining properties programmatically from its IBOutlet. This is more of a traditional way, tho :)
Hope this helps you!
You can override
var intrinsicContentSize: CGSize { get }
property of UIView and calculate and return the height and width of the view based on its contents.
Ref : intrinsicContentSize
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'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 :
I'm trying to get the real height of my custom UIButton. But I always get a value that is way smaller than I expected. I get something around 30 but expected something about 45.
override func awakeFromNib() {
print(self.frame.height)
self.layer.cornerRadius = self.layout.frame.height / 2.0
self.clipsToBounds = true
self.titleLabel?.adjustsFontSizeToFitWidth = true
}
This is my code, but at runtime somehow autolayout changes the size, which is perfect, but I can not set the right cornerRadius (always too small). So maybe I need the multiplier or something like that. I already testet with adding the contentEdgeInsets of top and bottom, but it made no difference.
Thanks
Tobias
The view hasn't been laid out in awakeFromNib(). Set your corner radius when layout has guaranteed to have happened.
For a view:
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = frame.height / 2.0
}
For a UIViewController:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.layer.cornerRadius = view.frame.height / 2.0
}
Try calling self.layoutIfNeeded() before you print the size