Constraints for dynamically size uilabel width programmatically - ios

I have two labels set completely programmatically. Label two should always be up against the right edge of label one (with a small space between them). Label one has its width set to be equal to its content size unless it hits a max width. Visually:
|Label one| |Label two|
I need the following constraints:
Label one should resize width wise unless it hits a max size.
Label two should always be up against the right edge of label one
How do I set these constraints programmatically?

lessThanOrEqualToConstant for the widthAnchor should do the job.
let labelOne = UILabel()
labelOne.text = "label1"
let labelTwo = UILabel()
labelTwo.text = "label2"
labelOne.translatesAutoresizingMaskIntoConstraints = false
labelTwo.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(labelOne)
view.addSubview(labelTwo)
NSLayoutConstraint.activate([
labelOne.topAnchor.constraint(equalTo: view.topAnchor),
labelOne.leadingAnchor.constraint(equalTo: view.leadingAnchor),
labelOne.widthAnchor.constraint(lessThanOrEqualToConstant: 100),
labelTwo.leadingAnchor.constraint(equalToSystemSpacingAfter: labelOne.trailingAnchor, multiplier: 1),
labelTwo.topAnchor.constraint(equalTo: view.topAnchor)
])

I agree with #Kevvv answer but you have to also assign trailing constraint of labelTwo to view's trailing, because if labelTwo width will increase if content size is more.
just add one or more constraint
labeltwo.trailingAnchor.constraint(greaterThanOrEqualTo: view.trailingAnchor)

Related

Profile Image cuts off in the UITableViewCell

I have a simple custom UITableViewCell which has profile image on the left, title and detailsLabel on the right. I have used Auto Layout constraints to set all the views on the screen. But the detailsLable text is short and profile image cuts off.
Let me know how to fix it. I could make the image small which is shorter than the height of title and label combined, but I want the big image.
// adding subviews
contentView.addSubview(profileImageView)
contentView.addSubview(nameLabel)
contentView.addSubview(jobTitleDetailedLabel)
// constraint for the views
profileImageView.topAnchor.constraint(equalTo:self.contentView.topAnchor, constant:10).isActive = true
profileImageView.leadingAnchor.constraint(equalTo:self.contentView.leadin gAnchor, constant:10).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant:50).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant:50).isActive = true
nameLabel.topAnchor.constraint(equalTo:self.contentView.topAnchor, constant:10).isActive = true
nameLabel.leadingAnchor.constraint(equalTo:self.profileImageView.trailingAnchor, constant:10).isActive = true
nameLabel.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor).isActive = true
jobTitleDetailedLabel.topAnchor.constraint(equalTo:self.nameLabel.bottomAnchor).isActive = true
jobTitleDetailedLabel.leadingAnchor.constraint(equalTo:self.profileImageView.trailingAnchor, constant:10).isActive = true
jobTitleDetailedLabel.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor).isActive = true
jobTitleDetailedLabel.bottomAnchor.constraint(equalTo:self.contentView.bottomAnchor, constant:-10).isActive = true
Just add jobTitleDetailedLabel height constrain to be greaterThanOrEqualToConstant profileImageView height + 10 for Margin
because if jobTitleDetailedLabel hight is less than Image hight it will make it small cell row
self.jobTitleDetailedLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
Make sure to constrain the cell's contentView's bottom anchor to be greaterThanOrEqualTo both the profileImageView's bottom anchor and the jobTitleDetailedLabel's bottom anchor. The cell will expand according to whichever is larger.
So you need to remove the jobTitleDetailedLabel's bottom anchor constraint and add:
contentView.bottomAnchor.constraint(greaterThanOrEqualTo: profileImageView.bottomAnchor, constant: 10).isActive = true
contentView.bottomAnchor.constraint(greaterThanOrEqualTo: jobTitleDetailedLabel.bottomAnchor, constant: 10).isActive = true
Also set the contentHuggingPriority of the nameLabel to high in order to ensure that the nameLabel keeps its intrinsic height (the height the text takes up).
nameLabel.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .vertical)
Finally set the cell's row height to UITableviewAutomaticDimension in the storyboard or through code - tableView.rowHeight = UITableViewAutomaticDimension

ios correct way to use constraintLessThanOrEqualToSystemSpacingAfter for trailingAnchor

I'd like to programmatically layout a UILabel that should fit the width of the screen, with the system spacing as left and right insets. Here's my code:
statusLabel.font = UIFont.systemFont(ofSize: UIFont.smallSystemFontSize)
statusLabel.numberOfLines = 0
statusLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(statusLabel)
statusLabel.topAnchor.constraint(equalTo: otherView.bottomAnchor, constant: 0),
statusLabel.leadingAnchor.constraintEqualToSystemSpacingAfter(view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
statusLabel.trailingAnchor.constraintLessThanOrEqualToSystemSpacingAfter(view.safeAreaLayoutGuide.trailingAnchor, multiplier: 1),
statusLabel.bottomAnchor.constraint(equalTo: someOtherView.topAnchor, constant: 0)
Here is the result:
the label is laid out using the system spacing as the left inset, as intended, but its trailingAnchor seems to be equal to the superview's trailingAnchor rather than adding a system spacing between the two.
I've tried using constraintEqualToSystemSpacingAfter and constraintGreaterThanOrEqualToSystemSpacingAfter but got the same results.
Any ideas on how to get the system spacing between the label and its superview's trailing anchors?
Reverse the order Like this
view.safeAreaLayoutGuide.trailingAnchor.constraintEqualToSystemSpacingAfter(statusLabel.trailingAnchor, multiplier: 1).isActive = true
view first & statusLabel next.

Subview (UILabel) doesn't respect the constraints when placed in superview (UITextView)

I tried my own "placeholder in a UITextView" implementation.
My approach was this:
I create a UILabel in a UITextView subclass, and I set the constraints of the UILabel to match the size of its superview (UITextView).
This is the code where I create the UILabel and assign it to a class variable named placeholderLabel in awakeFromNib():
placeholderLabel = UILabel()
placeholderLabel.text = placeholder
placeholderLabel.numberOfLines = 0
placeholderLabel.lineBreakMode = .byWordWrapping
placeholderLabel.textAlignment = .left
The following code is where I add the UILabel as a subview, and I set the constraints, again in awakeFromNib():
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
placeholderLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: textContainerInset.left + 4).isActive = true
placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: textContainerInset.top).isActive = true
placeholderLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: textContainerInset.right + 4).isActive = true
placeholderLabel.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: textContainerInset.bottom).isActive = true
I also have a property where I set the placeholder's text, where I have a didSet observer, which sets placeholderLabel's text and then calls layoutIfNeeded() in order to have the constraints recalculated in case the UILabel extends to a second (or third, etc) row:
var placeholder: String = "" {
didSet {
placeholderLabel.text = placeholder
layoutIfNeeded()
}
}
The issue is that I have the following result:
The UILabel extends beyond it's superviews bounds (to the right), and it appears that it doesn't respect the constraints. I run the visual debugger which confirmed the same thing:
It seems that there is a width constraint which follows the UILabel's content width instead of following the constraint I have set in place (in this case it creates a width of 431 whereas the superview's width is 288).
Is there something that I miss?
First of all you have to use a negative value for the right constraint's constant (or - to use a positive value - switch the items placeholderLabel.rightAnchor / rightAnchor).
The real problem though is the fact that UITextView is a subclass of UIScrollView. In your case adding the UILabel with a large text as a subview and constraining its edges to the textview's edges results in the textview's contentSize to grow. The textview becomes horizontally scrollable.
Printing out the textview's contentSize before and after adding the label results in different values for the width (before: 335.0, after: 505.0).
Proof: https://www.dropbox.com/s/eogvl2c5r76c6cl/example.mov?dl=0
You could work around that problem by not creating the right but a width constraint instead:
// placeholderLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -(textContainerInset.right + 4)).isActive = true
placeholderLabel.widthAnchor.constraint(equalTo: widthAnchor, constant: -(textContainerInset.left + 4 + textContainerInset.right + 4)).isActive = true
with right should be minus
placeholderLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: - textContainerInset.right - 4).isActive = true

UIScrollView how do you constrain a sub view that acts a container to all the other views?

so as you will see below I have a scrollview and I want to add it the the UIViewControllers root view. When I have it constrained to the top, right, bottom, and left I expect to see the red color take up the whole screen. This obviously works, but I want to add a subview to the scrollview that will wrap all the child views. How would I go about doing that?
I have added the view and I have set the same constraints except this time they are set from the wrapper view to the bounds of the UIScrollView, and the blue background color doesn't show anywhere. Also feel free to point out if this is a bad idea, but I thought I could just have it be constrained to the bottom and it will automatically extend the scrollviews content size as needed. This seems to work when I had all the subviews in the scrollview without a wrapper and the last view would extend the content size.
scrollView = UIScrollView(frame: view.bounds)
scrollView?.showsVerticalScrollIndicator = true
scrollView?.backgroundColor = .red
scrollView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView!)
scrollView?.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView?.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView?.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView?.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//setup wrapper view
let subviewWrapper = UIView()
subviewWrapper.translatesAutoresizingMaskIntoConstraints = false
scrollView?.addSubview(subviewWrapper)
subviewWrapper.backgroundColor = .blue
subviewWrapper.topAnchor.constraint(equalTo: (scrollView?.topAnchor)!).isActive = true
subviewWrapper.leftAnchor.constraint(equalTo: (scrollView?.leftAnchor)!).isActive = true
subviewWrapper.rightAnchor.constraint(equalTo: (scrollView?.rightAnchor)!).isActive = true
subviewWrapper.bottomAnchor.constraint(equalTo: (scrollView?.bottomAnchor)!).isActive = true
Actually this is a very good idea. I always set up my scrollViews this way. I usually call the view contentView, but it is the same idea.
You're almost there. You haven't yet given Auto Layout anything to go on to figure out the size of your subviewWrapper. The constraints you've set so far pin the subviewWrapper to the edges of the scrollView's content area, but this just establishes the fact that as the subviewWrapper grows, the content size of the scrollView will expand. Currently your subviewWrapper has 0 width and 0 height which is why you see no blue.
Below are 3 examples of how you might establish the size of your subviewWrapper.
Note: Each of the following examples is completely independent. Look at each one separately and as you try them, remember to delete the constraints added by the previous example.
Example 1: Make subviewWrapper 1000 x 1000:
Set constraints to make your subviewWrapper 1000 x 1000 and you will see the blue and it will scroll in both directions.
subviewWrapper.widthAnchor.constraint(equalToConstant: 1000).isActive = true
subviewWrapper.heightAnchor.constraint(equalToConstant: 1000).isActive = true
Example 2: Vertical only scrolling with content size 2X of scrollView height:
If you set the width of your subviewWrapper to be equal to the width of the scrollView then it will only scroll vertically. If you set the height of subviewWrapper to 2X the height of scrollView, then your blue area will be twice the height of the scrollView.
subviewWrapper.widthAnchor.constraint(equalTo: scrollView!.widthAnchor, multiplier: 1.0).isActive = true
subviewWrapper.heightAnchor.constraint(equalTo: scrollView!.heightAnchor, multiplier: 2.0).isActive = true
Example 3: Size of subviewWrapper set by its subviews:
You can also establish the size of your subviewWrapper by adding subviews to it that are fully specified in size and connected in a chain from the top of subviewWrapper to the bottom, and from side to side. If you do this, Auto Layout will have enough information to compute the size of your subviewWrapper
In this example, I've added a yellow 600 x 600 square to the subviewWrapper and set it 100 points from each edge. Without having explicitly set a size for subviewWrapper, Auto Layout can figure out that it is 800 x 800.
let yellowSquare = UIView()
yellowSquare.translatesAutoresizingMaskIntoConstraints = false
yellowSquare.backgroundColor = .yellow
subviewWrapper.addSubview(yellowSquare)
yellowSquare.widthAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.heightAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.topAnchor.constraint(equalTo: subviewWrapper.topAnchor, constant: 100).isActive = true
yellowSquare.leadingAnchor.constraint(equalTo: subviewWrapper.leadingAnchor, constant: 100).isActive = true
yellowSquare.trailingAnchor.constraint(equalTo: subviewWrapper.trailingAnchor, constant: -100).isActive = true
yellowSquare.bottomAnchor.constraint(equalTo: subviewWrapper.bottomAnchor, constant: -100).isActive = true

How to center vertically labels in a view

I have 4 labels like this in a view:
The view hierarchy like this:
But if one of text in each label is empty, all of other labels should center vertically with the image.
For example: the albumDataLabel.text is empty, then userNameLabel, albumNameLabel, albumLocationLabel should center vertically with the image.
Somethings like this:
So how to do this, please point me to some approaches.
Set height constraint for every label and which label have not text
make it's height zero(from outlet of height constraint by setting constant to 0) at runtime.
Your constraint should be in linear hierarchy like first label's top should be pinned with it's supper view's top and last label's bottom should be pinned with superview's bottom and each and every label's bottom should be pinned with top of below label.
then you should set height constraint for view that contains all labels with constant (>=) of minimum height(least height of your view).
and centered vertically that view with your image view.
you can do this kind of setup!!
Since your 4 Labels are already in a view, you can set the labels' constraints to pin the first Label to the top, last Label the bottom and spacing in between to zero
Then select the view(withLabels) and your ImageView to align their vertical centers
Do not set a height value constraint for your labels nor the view
When one of your labels have an empty string, the height is automatically set to zero and hence 'hidden' so the view(withLabels) will shrink in height. All can be done in the interface builder, no coding necessary, it is just a matter of autolayout.
1) for your userNameLabel:
userNameLabel.leftAnchor.constraintEqualToAnchor(imageView.rightAnchor, constant: 10).active = true
userNameLabel.topAnchor.constraintEqualToAnchor(self.topAnchor, constant: 50).active = true
userNameLabel.widthAnchor.constraintEqualToConstant(220).active = true
userNameLabel.heightAnchor.constraintEqualToConstant(30).active = true
2) for your albumNameLabel:
albumNameLabel.widthAnchor.constraintEqualToConstant(220).active = true
albumNameLabel.heightAnchor.constraintEqualToConstant(30).active = true
albumNameLabel.topAnchor.constraintEqualToAnchor(userNameLabel.bottomAnchor, constant: 5).active = true
albumNameLabel.leftAnchor.constraintEqualToAnchor(imageView.leftAnchor, constant: 10).active = true
3) remember this:
self.addSubview(userNameLabel)
self.addSubview(albumNameLabel)
And go on in this way to all elements in your View.

Resources