UIScrollView nested subviews not displayed using Auto Layout - ios

I am trying to generate views displayed in a UIScrollView based on structured (XML) data at runtime. I generate all the views I need and add them to my scrollview afterwards, using Auto Layout to let the scrollview calculate its contentSize. Everything worked fine as long as I was only using UILabels and a custom UIImageView subclass, but now I want to add a nested view containing another view and a label to represent text with a vertical line on the left side.
The general layout code I'm using to add all generated views to the scrollview is as follows:
var previousAnchor = scrollView.topAnchor
views.forEach { (view) in
scrollView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,
constant: UI.smallPadding).isActive = true
view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,
constant: -UI.smallPadding).isActive = true
previousAnchor = view.bottomAnchor
}
scrollView.bottomAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
And the layout code for my nested subview looks like this (it's a UIView subclass):
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor).isActive = true
line.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
line.widthAnchor.constraint(equalToConstant: 2).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
If I use this code, I cannot see this view in my scrollview at all and the scrollview has a warning of ambiguous content size attached to it in the Debug View Hierarchy view. This is somewhat irritating, as using labels without being nested in a view like this does work like a charm. So I started playing around and what really puzzles me is that if I use the following code I can see the view itself (as I gave it a gray background color), but still can't see line and/or label, as both seem have a frame of (0,0,0,0) upon inspection:
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
line.widthAnchor.constraint(equalToConstant: 20).isActive = true
line.heightAnchor.constraint(equalToConstant: 20).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
heightAnchor.constraint(equalToConstant: 100).isActive = true
heightAnchor.constraint(equalTo: label.heightAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
What am I missing? I also tried putting all my views into a container view instead of using them as subviews of the scrollview directly, but this did not help with my nested view and brought up other problems.

Related

ios Swift tableView dynamic cells constraints for labels and image programatically

I have labels and an image. I want labels above image. And image without leading and trailing margin constraints.
I have tried the following constraints but can't seem to get it right. And the row height for the cells doesn't adjust accordingly in landscape mode.
addSubview(videolabel1)
videolabel1.translatesAutoresizingMaskIntoConstraints = false
videolabel1.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
videolabel1.trailingAnchor.constraint(equalTo: trailingAnchor
, constant: 20).isActive = true
videolabel1.topAnchor.constraint(equalTo: topAnchor, constant: 20).isActive = true
videolabel1.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 20).isActive = true
addSubview(image)
image.translatesAutoresizingMaskIntoConstraints = false
image.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
image.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
image.heightAnchor.constraint(equalToConstant: 150).isActive = true
image.widthAnchor.constraint(equalTo: videoimage.heightAnchor
, multiplier: 16/9).isActive = true
image.topAnchor.constraint(equalTo: videodate.bottomAnchor, constant: 12).isActive = true
image.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12).isActive = true
Try using UIStackView's when you want to arrange views in a horizontal or vertical fashion. The UIStackView can take care of most constraints for you.
You can try adding a parent UIStackView with vertical orientation, that contains another UIStackView with horizontal orientation (this UIStackView will contain two UILabel's), followed by the UIImageView, and another UILabel.
parentStackView = UIStackView()
parentStackView.axis = .vertical
parentStackView.distribution = .equalSpacing
parentStackView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(parentStackView)
parentStackView.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
parentStackView.widthAnchor.constraint(equalTo: self.contentView.widthAnchor, multiplier: 1).isActive = true
parentStackView.heightAnchor.constraint(equalTo: self.contentView.heightAnchor, multiplier: 1).isActive = true
parentStackView.isLayoutMarginsRelativeArrangement = true
parentStackView.addArrangedSubview(stackView)
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.spacing = 10
stackView.addArrangedSubview(label)
label.text = "First Label";
stackView.addArrangedSubview(label2)
label2.text = "Second Label";
parentStackView.addArrangedSubview(imageView);
imageView.image = UIImage(named: "imageview");
NSLayoutConstraint.activate([imageView.heightAnchor.constraint(equalToConstant:UIScreen.main.bounds.width / 2) ]);
imageView.contentMode = .scaleAspectFill;
imageView.clipsToBounds = true;
parentStackView.addArrangedSubview(label3)
label3.text = "Vertical Label below Image";
The result will look something like:
Set bottom and top anchor according to sequential view on top and bottom. Here, you are setting bottom anchor of both videolabel1 and videodate to bottom anchor which is wrong:-
set videoLabel trailing and top anchor and remove bottom anchor:-
videolabel1.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
videolabel1.topAnchor.constraint(equalTo: topAnchor, constant: 20).isActive = true
set videoLabel trailing anchor as below and remove bottom anchor:-
videodate.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
Also, remove bottom anchor of imagview and set bottom anchor constant as shown below:-
videotitle.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12).isActive = true

Why width of view always return zero?

I'm setting up a view programmatically with all the constraints. Then I want to get its width (which vary from a device to another) but it always return zero when I print it.
here is my code:
let myView = UIView()
view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15).isActive = true
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60).isActive = true
myView.backgroundColor = .blue
let myWidth1 = myView.frame.size.width
let myWidth2 = myView.bounds.width
print(myWidth1)
print(myWidth2)
however, the view actually has a width as shown in this screenshot:
For programmatic development, you should create that view in loadView(). viewDidLoad() is the lifecycle method that is called after the view has been loaded into memory; however, the view hasn't yet been rendered visually. You can get subview dimensions in viewDidLayoutSubviews(), which will be called throughout view mutations (like device orientation changes) or a method like viewDidAppear() that will only be called effectively once.

How to programmatically place two labels in center side by side with spacing using auto layout in ios swift?

I am very new to swift and IOS and I want to place two labels side by side horizontally center programmatically. Below is my code
let secondLabel : UILabel = {
let label = UILabel()
label.text = "1234"
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.blue
return label
}()
let thirdLabel : UILabel = {
let label = UILabel()
label.text = "5678"
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.red
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupLayout()
}
private func setupLayout(){
view.addSubview(secondLabel)
//secondLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
secondLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
view.addSubview(thirdLabel)
//thirdLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
thirdLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
thirdLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
thirdLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
self.view.addConstraint(NSLayoutConstraint(
item: thirdLabel,
attribute: .left,
relatedBy: .equal,
toItem: secondLabel,
attribute: .right,
multiplier: 1.0,
constant: 10
))
}
But this is how it looks now
Please help me to move the labels to the center
You can use UIStackView to solve your problem. Please update the following code in your setupLayout method.
private func setupLayout(){
let stackview = UIStackView()
stackview.axis = .horizontal
stackview.spacing = 10
stackview.translatesAutoresizingMaskIntoConstraints = false
stackview.addArrangedSubview(secondLabel)
secondLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
stackview.addArrangedSubview(thirdLabel)
thirdLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
thirdLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
self.view.addSubview(stackview)
stackview.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
Output:-
Well UIStackView will be the best option and #manikandan has answered properly.
But if you are not a big fan of UIStackView, you can achieve it using UIView too. A bit more lines of code and it will work.
private func setupLayout() {
let sampleBackgroundView = UIView()
sampleBackgroundView.translatesAutoresizingMaskIntoConstraints = false
sampleBackgroundView.addSubview(secondLabel)
secondLabel.topAnchor.constraint(equalTo: secondLabel.superview!.topAnchor).isActive = true
secondLabel.bottomAnchor.constraint(equalTo: secondLabel.superview!.bottomAnchor).isActive = true
secondLabel.leadingAnchor.constraint(equalTo: secondLabel.superview!.leadingAnchor, constant: 10).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
sampleBackgroundView.addSubview(thirdLabel)
thirdLabel.topAnchor.constraint(equalTo: secondLabel.superview!.topAnchor).isActive = true
thirdLabel.bottomAnchor.constraint(equalTo: secondLabel.superview!.bottomAnchor).isActive = true
thirdLabel.trailingAnchor.constraint(equalTo: secondLabel.superview!.trailingAnchor, constant: 10).isActive = true
thirdLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
thirdLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
sampleBackgroundView.addConstraint(NSLayoutConstraint(
item: thirdLabel,
attribute: .left,
relatedBy: .equal,
toItem: secondLabel,
attribute: .right,
multiplier: 1.0,
constant: 10
))
view.addSubview(sampleBackgroundView)
sampleBackgroundView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
sampleBackgroundView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
Please go through these solutions and you will have a better idea to implement constraints programmatically:
How to add constraints programmatically using Swift
https://theswiftdev.com/2018/06/14/mastering-ios-auto-layout-anchors-programmatically-from-swift/
https://theswiftdev.com/2017/10/31/ios-auto-layout-tutorial-programmatically/
While a UIStackView is one way to go (and easier many times), there are also times that you want to do what you are trying. Truth is, you're almost there.
private func setupLayout(){
view.addSubview(secondLabel)
secondLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -55).isActive = true
secondLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
view.addSubview(thirdLabel)
thirdLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 55).isActive = true
thirdLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
thirdLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
thirdLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
Basically, it looks like you have two 100x100 UILabels that you want 10 points apart. So:
Get rid of that last constraint.
Comment back in those two centerXAnchor constraints. This will move both labels on top of each other, centered horizontally.
Now you just need to add a constant property to each constraint. A negative value move to the left and a positive value moves to the right. Since your widths are 100, a value of 50should make the labels be right next to each. For 10 points in between them, make it 55.
There're two options for you to work on that (or maybe more).
Create Additional containerView(UIView) to addSubView your 2 labels. Then centerYAnchor that containerView to your baseView.
Using UIStackView. Add those 2 labels into your Horizontal StackView and centerYAnchor to your baseView
Simple explanation hopes this helps.
Using a container view (either UIView, or UIStackView) might be the best approach, as it would allow you to easier build other UI components around the group of two labels. But if you don't need this flexibility, or want to avoid using an extra view, you can achieve the desired layout quite nicely via layout anchors:
NSLayoutConstraint.activate([
// set the size
secondLabel.widthAnchor.constraint(equalToConstant: 100),
secondLabel.heightAnchor.constraint(equalToConstant: 100),
// center vertically in the parent view
secondLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
// align to the left of the parent view center
secondLabel.rightAnchor.constraint(equalTo: view.centerXAnchor, constant: -5),
// set the size
thirdLabel.widthAnchor.constraint(equalToConstant: 100),
thirdLabel.heightAnchor.constraint(equalToConstant: 100),
// center vertically in the parent view
thirdLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
// align to the right of the parent view center
thirdLabel.leftAnchor.constraint(equalTo: view.centerXAnchor, constant: 5)
])
The spacing between the two is automatically achieved via the offsets from the X center.
As a general note, Apple recommends activating constraints in batches, by using activate(), for performance reasons:
The effect of this method is the same as setting the isActive property of each constraint to true. Typically, using this method is more efficient than activating each constraint individually.

Swift: I'm not able to make autolayout constraints work for my UIScrollView

I'm trying to make a simple layout that should scroll vertically when views are too many to fit on the screen.
So this is what I have so far:
I created a scrollview and a container view like that
let mainScrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.isUserInteractionEnabled = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let containerView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
Then I added the scrollview to the main view, added the container view to the scrollview and a couple of labels to the container view
view.addSubview(mainScrollView)
mainScrollView.addSubview(containerView)
containerView.addSubview(firstLabel)
containerView.addSubview(secondLabel)
I pinned the scrollview to the main view, and the container view to the scrollview. After that I started adding the labels inside the container view like this
mainScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mainScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mainScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mainScrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: mainScrollView.layoutMarginsGuide.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: mainScrollView.layoutMarginsGuide.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
firstLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
firstLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 30).isActive = true
firstLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
firstLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
secondLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: 750).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
The problem is that for some reason the scrollview is not able to calculate it's height and it doesn't scroll vertically. The second label remains invisible because it's too "low" on the screen.
I tried setting the bottom constraint of the container view to the bottom of the second label but I'm going nowhere honestly.
What am I missing? How can I set constraints to make the scrollview scroll with autolayout (without setting a specific height of the container view)?
Scrollview scrolls in either direction. So any view added to it would need some additional constraints to let the Scrollview calculate its content size.
You have pinned the container view to scroll view. If you are looking at a vertically scrolling view like a table view then you need to also set the width of the scroll to the container view so that it can calculate the width.
Next for height, its automatically calculated based on the UI elements added in the container view. Ensure that all of the labels have leading, trailing to the container and vertical spacing to each other and top of the container view. This will let the scroll view know the height needed.
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.translatesAutoresizingMaskIntoConstraints = false
self.label1.translatesAutoresizingMaskIntoConstraints = false
self.label2.translatesAutoresizingMaskIntoConstraints = false
self.label3.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.label1)
self.contentView.addSubview(self.label2)
self.contentView.addSubview(self.label3)
self.scrollView.addSubview(self.contentView)
self.view.addSubview(self.scrollView)
NSLayoutConstraint.activate([
self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
])
NSLayoutConstraint.activate([
self.contentView.topAnchor.constraint(equalTo: self.scrollView.topAnchor),
self.contentView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor),
self.contentView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor),
self.contentView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor),
self.contentView.widthAnchor.constraint(equalTo: self.scrollView.widthAnchor)
])
NSLayoutConstraint.activate([
self.label1.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
self.label2.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
self.label3.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
self.label1.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
self.label2.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
self.label3.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
])
NSLayoutConstraint.activate([
self.label1.topAnchor.constraint(equalTo: self.contentView.topAnchor),
self.label2.topAnchor.constraint(equalTo: self.label1.bottomAnchor),
self.label3.topAnchor.constraint(equalTo: self.label2.bottomAnchor),
self.label3.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
])
And when run, it won't complain about any constraint issues either. Same can be done for horizontal scroll.
Thank you very much #GoodSp33d, you pointed me to the right direction.
I added the width constraint as you said like that:
containerView.widthAnchor.constraint(equalTo: mainScrollView.widthAnchor).isActive = true
And the key point was to update the bottom constraint after all:
containerView.bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor).isActive = true
Here is the full working code:
mainScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mainScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mainScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mainScrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: mainScrollView.topAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: mainScrollView.trailingAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: mainScrollView.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: mainScrollView.leadingAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: mainScrollView.widthAnchor).isActive = true
firstLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
firstLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 30).isActive = true
firstLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
firstLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
secondLabel.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: 550).isActive = true
secondLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
secondLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
containerView.bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor).isActive = true
Thanks for the help.

Height is ambiguous

Using Xcode 8.0 (8A218a)
I get "Height is ambiguous." even though (in my opinion) I've got constraints all along the Y-axis.
Both of the two subviews inside the view have a height (one have height as constraint, and one as intristic content size).
See the image below for what the "Debug View Hierarchy" says.
The constraints are created in code with the following code:
label is UILabel, underline is UIView
label.translatesAutoresizingMaskIntoConstraints = false
underline.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
addSubview(underline)
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
label.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: underline.topAnchor, constant: -5).isActive = true
underline.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
underline.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
underline.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
underline.heightAnchor.constraint(equalToConstant: 2.0).isActive = true
Here's sample-project extracted from the problematic project:
https://github.com/everlof/LayoutIssue

Resources