Programmatically add views one after another - ios

I am new to Swift (using 4 version).
I have HTML text, that can contain pictures links inside.
some text
<img src=""/>
some text
I want to make content scrollable, so I wrapped container (simple UIView) into UIScrollview.
Then I parse HTML text into parts: "text" and "image links" into one array (data and type).
Then iterating through that array I watch, if the type is text - I input label with constraints:
If it's the first element of the container, then left, top, right, and bottom are match.
If not, then top matches the previous view's bottom in a container (like chain).
But for some reason, it's not working.
What am I doing wrong?
I put my code:
var lastView = containerView!
var label : UILabel
var imageView : UIImageView
for item in articleContent {
if (item.type == RenderDetailBlogObject.TYPE_TEXT) {
label = UILabel()
label.numberOfLines = 0
label.attributedText = item.data.html2AttributedString
containerView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leftAnchor.constraint(equalTo: containerView.leftAnchor),
label.rightAnchor.constraint(equalTo: containerView.rightAnchor),
])
if (lastView == containerView) {
print("ADDING LABEL lastView == containerView")
label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
} else {
print("ADDING LABEL lastView != containerView")
label.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
}
lastView = label
} else {
imageView = UIImageView()
imageView.sd_setImage(with: URL(string: item.data))
containerView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.leftAnchor.constraint(equalTo: containerView.leftAnchor),
imageView.rightAnchor.constraint(equalTo: containerView.rightAnchor),
])
if (lastView == containerView) {
print("ADDING AN IMAGE lastView == containerView")
imageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
} else {
print("ADDING AN IMAGE lastView != containerView")
imageView.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
}
lastView = imageView
}
lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0)
}
EDIT:
Code seems to be right and views are adding correctly, but contraints not letting to scroll view.
I tested constraint (center Y - added 200 more range to it and it started to scroll, but it's hardcoded value, so may be it has more legit solution?)
Without that constraint it's giving error and not working at all.

I believe this line is your source of trouble:
lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0)
Right now, when you iterate through items, you constrain each label/imageView to have bottom anchor constrained to bottom of the containerView. I would expect that this would cause Unsatisfiable constraints warning (check your console). You want to constrain the bottom of the last label/imageView that you add to the container. Therefore just move that line out of the for loop right behind it:
var lastView = containerView!
var label : UILabel
var imageView : UIImageView
for item in articleContent {
if (item.type == RenderDetailBlogObject.TYPE_TEXT) {
label = UILabel()
label.numberOfLines = 0
label.attributedText = item.data.html2AttributedString
containerView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leftAnchor.constraint(equalTo: containerView.leftAnchor),
label.rightAnchor.constraint(equalTo: containerView.rightAnchor),
])
if lastView == containerView {
print("ADDING LABEL lastView == containerView")
label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
} else {
print("ADDING LABEL lastView != containerView")
label.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
}
lastView = label
} else {
imageView = UIImageView()
imageView.sd_setImage(with: URL(string: item.data))
containerView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.leftAnchor.constraint(equalTo: containerView.leftAnchor),
imageView.rightAnchor.constraint(equalTo: containerView.rightAnchor),
])
if lastView == containerView {
print("ADDING AN IMAGE lastView == containerView")
imageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0).isActive = true
} else {
print("ADDING AN IMAGE lastView != containerView")
imageView.topAnchor.constraint(equalTo: lastView.bottomAnchor, constant: 0.0).isActive = true
}
lastView = imageView
}
}
// Constrain the bottom only for the last view that was added
lastView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0).isActive = true
If refactoring according to this answer does not solve the scrolling problem, I recommend checking out the constraints between the containerView and the scrollView - you can use this answer as a reference.

Perhaps a much cleaner approach would be to use a UIStackView inside your scrollview such that you can add any other view as a child view to UIStackView. this way you don't have to deal with the constraints yourself.
So your view hierarchy should be similar to something like below.
->UIView
->UIScrollView
->UIView
->UIStackView
Then add constraints to scrollView as top-left-bottom-right
Also add another UIView to UIScrollView as Wrapper View and add constraint as top-left-bottom-right and add two more constraint to this wrapper view Equals width and Equals height with respect to main view. This will make UIScrollView work with dynamic height, more info in this post
Now add constraint to stack view as top-left-right-bottom also add a height constraint with an outlet connection.
Now when you add any view to the stack view just change the height constant of UIStackView and your UIScrollView will expand itself automatically.

Related

(Swift 5) UIScrollView scrolls but none of the content scrolls (video included)

I'm trying to learn to build views without storyboard. I tried to build a scrollview. On that scrollview is a UISearchBar, a UIImageView with an image and a UILabel. It works but none of the content moves. The content is all just frozen in place like no matter how far I scroll the search bar will always be on top of the page. and the image on the bottom. I've attached a video to show what I mean. There's also a problem because none of the content is where I want it to be but that's another problem. I realize this is probably because I don't know enough about constraints and autolayout and building views without storyboards.
Here's the video
class HomePageViewController: UIViewController {
var searchedText: String = ""
let label = UILabel()
let searchBar: UISearchBar = {
let searchBar = UISearchBar()
searchBar.placeholder = "Where are you going?"
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBar.barTintColor = .systemCyan
searchBar.searchTextField.backgroundColor = .white
searchBar.layer.cornerRadius = 5
return searchBar
}()
let homeImage: UIImageView = {
let homeImage = UIImageView()
homeImage.translatesAutoresizingMaskIntoConstraints = false
homeImage.clipsToBounds = true
return homeImage
}()
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .systemMint
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * 30)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
// setupLayout()
// tried this here doesn't do anything for me
}
func setupLayout() {
view.addSubview(scrollView)
self.scrollView.addSubview(searchBar)
homeImage.image = UIImage(named: "Treehouse")
self.scrollView.addSubview(homeImage)
label.text = "Inspiration for your next trip..."
self.scrollView.addSubview(label)
// not sure where this label is being added I want it to be underneath the image but it isn't t
let safeG = view.safeAreaLayoutGuide
let viewFrame = view.bounds
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: -10),
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
searchBar.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 50.0),
searchBar.widthAnchor.constraint(equalTo: safeG.widthAnchor, multiplier: 0.9),
searchBar.centerXAnchor.constraint(equalTo: safeG.centerXAnchor),
homeImage.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 150),
homeImage.widthAnchor.constraint(equalTo: safeG.widthAnchor, multiplier: 1.1),
homeImage.centerXAnchor.constraint(equalTo: safeG.centerXAnchor),
homeImage.heightAnchor.constraint(equalToConstant: viewFrame.height/2),
label.topAnchor.constraint(equalTo: homeImage.bottomAnchor, constant: 100)
])
// was doing all this in viewDidLayoutSubviews but not sure if this is better place for it
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupLayout()
// tried this in viewDidLoad() and it didn't solve it.
}
}
any help would be appreciated
First, when constraining subviews in a UIScrollView, you should constrain them to the scroll view's Content Layout Guide. You're constraining them to the view's safe area layout guide, so they're never going to go anywhere.
Second, it's difficult to center subviews in a scroll view, because the scroll view can scroll both horizontally and vertically. So it doesn't really have a "center."
You can either put subviews in a stack view, or, quite common, use a UIView as a "content" view to hold the subviews. If you constrain that content view's Width to the scroll view's Frame Layout Guide width, you can then horizontally center the subviews.
Third, it can be very helpful to comment your constraints, so you know exactly what you expect them to do.
Here's a modified version of your posted code:
class HomePageViewController: UIViewController {
var searchedText: String = ""
let label: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let searchBar: UISearchBar = {
let searchBar = UISearchBar()
searchBar.placeholder = "Where are you going?"
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBar.barTintColor = .systemCyan
searchBar.searchTextField.backgroundColor = .white
searchBar.layer.cornerRadius = 5
return searchBar
}()
let homeImage: UIImageView = {
let homeImage = UIImageView()
homeImage.translatesAutoresizingMaskIntoConstraints = false
homeImage.clipsToBounds = true
return homeImage
}()
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .systemMint
// don't do this
//scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * 30)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
setupLayout()
}
func setupLayout() {
view.addSubview(scrollView)
//homeImage.image = UIImage(named: "Treehouse")
homeImage.image = UIImage(named: "natureBKG")
label.text = "Inspiration for your next trip..."
// let's use a UIView to hold the "scroll content"
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
// give it a green background so we can see it
contentView.backgroundColor = .green
contentView.addSubview(searchBar)
contentView.addSubview(homeImage)
contentView.addSubview(label)
scrollView.addSubview(contentView)
let safeG = view.safeAreaLayoutGuide
let svContentG = scrollView.contentLayoutGuide
let svFrameG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView to all 4 sides of view
// (generally, constrain to safe-area, but this is what you had)
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// constrain contentView to all 4 sides of scroll view's Content Layout Guide
contentView.topAnchor.constraint(equalTo: svContentG.topAnchor, constant: 0.0),
contentView.leadingAnchor.constraint(equalTo: svContentG.leadingAnchor, constant: 0.0),
contentView.trailingAnchor.constraint(equalTo: svContentG.trailingAnchor, constant: 0.0),
contentView.bottomAnchor.constraint(equalTo: svContentG.bottomAnchor, constant: 0.0),
// constrain contentView Width equal to scroll view's Frame Layout Guide Width
contentView.widthAnchor.constraint(equalTo: svFrameG.widthAnchor),
// constrain searchBar Top to contentView Top + 50
searchBar.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 50.0),
// constrain searchBar Width to 90% of contentView Width
searchBar.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.9),
// constrain searchBar centerX to contentView centerX
searchBar.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain homeImage Top to searchBar Bottom + 40
homeImage.topAnchor.constraint(equalTo: searchBar.bottomAnchor, constant: 40.0),
// constrain homeImage Width equal to contentView Width
homeImage.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 1.0),
// constrain homeImage centerX to contentView centerX
homeImage.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain homeImage Height to 1/2 of scroll view frame Height
homeImage.heightAnchor.constraint(equalTo: svFrameG.heightAnchor, multiplier: 0.5),
// you probably won't get vertical scrolling yet, so increase the vertical space
// between the homeImage and the label by changing the constant
// from 100 to maybe 400
// constrain label Top to homeImage Bottom + 100
label.topAnchor.constraint(equalTo: homeImage.bottomAnchor, constant: 100.0),
// constrain label centerX to contentView centerX
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain label Bottom to contentView Bottom - 20
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20.0),
])
}
}

Setting a bottom constraint to a UITextView if needed

I'm trying to fix an issue with a UItextview which I placed at the bottom of a viewcontroller programmatically and sometimes it can clip through the bottom of the view if I don't set a constraint like so.
https://i.stack.imgur.com/YfyPi.png
Whenever I try to constraint the textview to the bottom of the safe area, the text needlessly expands too much if there's less text.
https://i.stack.imgur.com/8w1v2.png
Here's the relevant code snippets from the textview and the constraints respectively:
private let summaryTextView: UITextView = {
let summaryTextView = UITextView()
summaryTextView.textColor = .label
summaryTextView.backgroundColor = .customWhite
summaryTextView.textAlignment = .center
summaryTextView.font = UIFont.systemFont(ofSize: 24)
summaryTextView.clipsToBounds = true
summaryTextView.layer.cornerRadius = 20
summaryTextView.layer.masksToBounds = true
summaryTextView.isSelectable = false
summaryTextView.isEditable = false
summaryTextView.isScrollEnabled = false
summaryTextView.translatesAutoresizingMaskIntoConstraints = false
return summaryTextView
}()
private func setupConstraints() {
NSLayoutConstraint.activate([
imageContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 120),
imageContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
imageContainerView.heightAnchor.constraint(equalToConstant: 280),
imageContainerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.4),
summaryTextView.topAnchor.constraint(equalTo: imageContainerView.bottomAnchor,constant: 15),
summaryTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 10),
summaryTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -10),
summaryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
backgroundImage.fillSuperView(to: view)
bookCover.fillSuperView(to: imageContainerView)
}
Any help would be appreciated!
You need to set the height constraint for UITextView(). Because you are giving fixed top anchor and and bottom anchor so it stretches the textview.

How to embed a UIViewController's view into another view without breaking responder chain

I'm trying to implement a base class for View Controllers that will add a top banner that is shown and hidden based on a specific status. Because my UIViewController extend this base class the view property is already loaded by the time I have to embed the original view property into a new view property that is created programmatically.
The embedding works fine (UI wise) the views adjust themselves appropriately however the issue that I'm seeing is that after embedding it the embedded views no longer respond to touch events, in the controller that I'm originally embedding I've got a table view with 16 rows and a button, before embedding it they both respond to tap and scroll events correctly.
I'm constrained to the fact that I cannot use a split view controller in IB to achieve the dual view split.
Here is my current implementation, can't seem to figure out what I'm missing to have event's propagate, I've tried using a custom view that overrides hitTest() to no avail for both newRootView and contentView variables.
Any help or insights are really appreciated, thanks!
class BaseViewController: UIViewController {
var isInOfflineMode: Bool {
didSet { if isInOfflineMode { embedControllerView() }}
}
var offlineBanner: UIView!
var contentView: UIView!
private func embedControllerView() {
guard let currentRoot = viewIfLoaded else { return }
currentRoot.translatesAutoresizingMaskIntoConstraints = false
let newRootView = UIView(frame: UIScreen.main.bounds)
newRootView.backgroundColor = .yellow // debugging
newRootView.isUserInteractionEnabled = true
newRootView.clipsToBounds = true
view = newRootView
offlineBanner = createOfflineBanner() // returns a button that I've verified to be tapable.
view.addSubview(offlineBanner)
contentView = UIView(frame: .zero)
contentView.backgroundColor = .cyan // debugging
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.isUserInteractionEnabled = true
view.addSubview(contentView)
contentView.addSubview(currentRoot)
let bannerHeight: CGFloat = 40.00
var topAnchor: NSLayoutYAxisAnchor
var bottomAnchor: NSLayoutYAxisAnchor
var trailingAnchor: NSLayoutXAxisAnchor
var leadingAnchor: NSLayoutXAxisAnchor
if #available(iOS 11.0, *) {
topAnchor = view.safeAreaLayoutGuide.topAnchor
bottomAnchor = view.safeAreaLayoutGuide.bottomAnchor
leadingAnchor = view.safeAreaLayoutGuide.leadingAnchor
trailingAnchor = view.safeAreaLayoutGuide.trailingAnchor
} else {
topAnchor = view.topAnchor
bottomAnchor = view.bottomAnchor
leadingAnchor = view.leadingAnchor
trailingAnchor = view.trailingAnchor
}
NSLayoutConstraint.activate([
offlineBanner.heightAnchor.constraint(equalToConstant: bannerHeight),
offlineBanner.topAnchor.constraint(equalTo: topAnchor),
offlineBanner.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
offlineBanner.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
])
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor, constant: bannerHeight),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
])
OfflineViewController.migrateViewContraints(from: currentRoot, to: contentView)
view.setNeedsUpdateConstraints()
offlineBanner.setNeedsUpdateConstraints()
currentRoot.setNeedsUpdateConstraints()
}
private func unembedControllerView() {
let v = contentView.subviews[0]
v.removeFromSuperview()
view = v
OfflineViewController.migrateViewContraints(from: contentView, to: v)
}
/**
Replaces any constraints associated with the current root's safe area`UILayoutGuide` or with the actual
current root view.
*/
private static func migrateViewContraints(from currentRoot: UIView, to newRoot: UIView) {
for ct in currentRoot.constraints {
var firstItem: Any? = ct.firstItem
var secondItem: Any? = ct.secondItem
if #available(iOS 11.0, *) {
if firstItem as? UILayoutGuide == currentRoot.safeAreaLayoutGuide {
debugPrint("Migrating firstItem is currentLayoutGuide")
firstItem = newRoot.safeAreaLayoutGuide
}
if secondItem as? UILayoutGuide == currentRoot.safeAreaLayoutGuide {
debugPrint("Migrating secondItem is currentLayoutGuide")
secondItem = newRoot.safeAreaLayoutGuide
}
}
if firstItem as? UIView == currentRoot {
debugPrint("Migrating firstItem is currentRoot")
firstItem = newRoot
}
if secondItem as? UIView == currentRoot {
debugPrint("Migrating secondItem is currentRoot")
secondItem = newRoot
}
NSLayoutConstraint.deactivate([ct])
NSLayoutConstraint.activate([
NSLayoutConstraint(item: firstItem as Any,
attribute: ct.firstAttribute,
relatedBy: ct.relation,
toItem: secondItem,
attribute: ct.secondAttribute,
multiplier: ct.multiplier,
constant: ct.constant)
])
}
}
}
In this specific view the green buttons does get events it's a button that I create programmatically:
And here's the view that does not respond to events, a table view with a button:
I've figured what the issue was; I was missing constraints between contentView (the new root view) and the original self.view. The constraints essentially made the original currentRoot view take the full space of contentView:
NSLayoutConstraint.activate([
currentRoot.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
currentRoot.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
currentRoot.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
currentRoot.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
])

Swift: Programmatically set autolayout constraints for subviews don't resize view

I have the following setup: A UIView adds a bunch of subviews (UILabels) programmatically, and sets also the autolayout constraints, so that the distance between the labels and between the UIViews edges is 10 each. The goal is that the UIView sets its size accordingly to the content of all the subviews (labels with dynamic text) including the spaces.
I use the following code, but it seems not to work. The UIView doesn't resize, it shrinks the labels.
// setup of labelList somewhere else, containing the label data
var lastItemLabel: UILabel? = nil
var i = 1
for item in itemList {
let theLabel = UILabel()
// ... label setup with text, fontsize and color
myView.addSubview(theLabel)
theLabel.translatesAutoresizingMaskIntoConstraints = false
// If it is the second or more
if let lastLabel = lastItemLabel {
theLabel.leadingAnchor.constraint(equalTo: lastLabel.trailingAnchor, constant: 12).isActive = true
theLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 10).isActive = true
// if it is the last label
if i == labelList.count {
theLabel.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 12).isActive = true
}
}
// If it is the first label
else {
theLabel.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 12).isActive = true
theLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 10).isActive = true
}
lastItemLabel = theLabel
i += 1
}
Since you need your content to be larger than the physical display of the device, you will need to add a UIScrollView to contain your labels.

Autolayout: Center two views with Constraint Inequalities

I want to programmatically center two or more views within a container using only constraint inequalities. It would look something like this:
I have achieved this behavior using another view to group both labels and setting its centerXAnchor equal to the superview's centerXAnchor. I'm also aware that it's possible to achieve the same goal using two spacer views. The problem is that my application will have quite a lot of views, so I'd like to use as few subviews as possible in order to save resources
I have tried using constraint inequalities (first label's leading anchor >= view's leading anchor and second label's trailing anchor <= view's trailing anchor) to center these two views but the result I get is this.
code:
class TestView: UIView {
let firstLabel = UILabel()
let secondLabel = UILabel()
init() {
super.init(frame: .zero)
self.layer.borderColor = UIColor.gray.cgColor
self.layer.borderWidth = 1
setupSubviewsConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviewsConstraints() {
self.addSubview(firstLabel)
self.addSubview(secondLabel)
firstLabel.layer.borderColor = UIColor.yellow.cgColor
firstLabel.layer.borderWidth = 1
firstLabel.text = "first"
firstLabel.translatesAutoresizingMaskIntoConstraints = false
firstLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 5).isActive = true
firstLabel.leadingAnchor.constraint(greaterThanOrEqualTo: self.leadingAnchor, constant: 5).isActive = true
firstLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -5).isActive = true
secondLabel.layer.borderColor = UIColor.blue.cgColor
secondLabel.layer.borderWidth = 1
secondLabel.text = "second"
secondLabel.translatesAutoresizingMaskIntoConstraints = false
secondLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 5).isActive = true
secondLabel.leadingAnchor.constraint(equalTo: firstLabel.trailingAnchor, constant: 5).isActive = true
secondLabel.trailingAnchor.constraint(lessThanOrEqualTo: self.trailingAnchor, constant: -5).isActive = true
secondLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -5).isActive = true
}

Resources