Using Swift Auto Layout for Dynamic and Fixed UI Elements
Position two UILabel instances in code where the top label miantains a fixed size and the bottom label wraps text based on its content using Auto Layout.
import UIKit
class LayoutController: UIViewController {
let headerLbl: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = "Header"
lbl.textAlignment = .center
lbl.backgroundColor = .systemGray5
return lbl
}()
let bodyLbl: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = "Resizable content that wraps across multiple lines depending on length. Resizable content that wraps across multiple lines depending on length."
lbl.numberOfLines = 0
lbl.textAlignment = .center
lbl.backgroundColor = .systemYellow
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(headerLbl)
view.addSubview(bodyLbl)
applyVerticalConstraints()
}
func applyVerticalConstraints() {
NSLayoutConstraint.activate([
headerLbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
headerLbl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
headerLbl.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
headerLbl.heightAnchor.constraint(equalToConstant: 50)
])
NSLayoutConstraint.activate([
bodyLbl.topAnchor.constraint(equalTo: headerLbl.bottomAnchor, constant: 20),
bodyLbl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
bodyLbl.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
bodyLbl.bottomAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
}
}
For a side-by-side arrangement, anchor labels horizontally with one having a constant width:
let sideALbl: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = "Left"
lbl.textAlignment = .center
lbl.backgroundColor = .systemGray4
return lbl
}()
let sideBLbl: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = "Right area can expand vertically if needed. Right area can expand vertically if needed."
lbl.numberOfLines = 0
lbl.textAlignment = .center
lbl.backgroundColor = .systemOrange
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(sideALbl)
view.addSubview(sideBLbl)
applyHorizontalConstraints()
}
func applyHorizontalConstraints() {
NSLayoutConstraint.activate([
sideALbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
sideALbl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
sideALbl.widthAnchor.constraint(equalToConstant: 100),
sideALbl.heightAnchor.constraint(equalToConstant: 50)
])
NSLayoutConstraint.activate([
sideBLbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
sideBLbl.leadingAnchor.constraint(equalTo: sideALbl.trailingAnchor, constant: 20),
sideBLbl.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
sideBLbl.bottomAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
}
To allocate proportional widths between labels, use a multiplier relative to the parent view:
func applyProportionalWidths() {
NSLayoutConstraint.activate([
sideALbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
sideALbl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
sideALbl.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3),
sideALbl.heightAnchor.constraint(equalToConstant: 50)
])
NSLayoutConstraint.activate([
sideBLbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
sideBLbl.leadingAnchor.constraint(equalTo: sideALbl.trailingAnchor, constant: 20),
sideBLbl.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
sideBLbl.bottomAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
}