I have a view with 3 dynamic labels inside it and I am trying to find a way to centre it vertically in a scroll view but when its dynamic labels are too large to fit on a pa
You can accomplish this by embedding the labels in a stack view and embedding the stack view in a UIView. The label text will expand the stack view vertically, which will expand the content view vertically, which will control the scroll view's .contentSize.
Black is the scroll view; blue is the content view; stack view only shows as thin gray outline; labels are yellow, green and cyan. The background colors just make it easier to see what's what.
Bunch of steps, but should be clear:
0 for top/leading/trailing/bottom of contentView to scrollViewVertical / Fill / Fill / Spacing: 20>= 8Setting the height Priority of the contentView to 250 will allow it to expand vertically based on the text in the labels.
Setting top and bottom stackView constraints to >= 8 will "push" the top and bottom of the contentView, but allow extra space when you don't have enough text to exceed the vertical bounds.
Results:
Here's a storyboard with everything in place for reference:
And here's a quick example replicating that layout / functionality via code only:
//
// ScrollWorkViewController.swift
//
// Created by DonMag on 6/12/19.
//
import UIKit
class ScrollWorkViewController: UIViewController {
let theScrollView: UIScrollView = {
let v = UIScrollView()
v.backgroundColor = .red
return v
}()
let contentView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(red: 0.25, green: 0.25, blue: 1.0, alpha: 1.0)
return v
}()
let stackView: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.alignment = .fill
v.distribution = .fill
v.spacing = 20
return v
}()
let topLabel: UILabel = {
let v = UILabel()
v.font = UIFont.boldSystemFont(ofSize: 32.0)
v.backgroundColor = .yellow
return v
}()
let centerLabel: UILabel = {
let v = UILabel()
v.font = UIFont.systemFont(ofSize: 17.0)
v.numberOfLines = 0
v.backgroundColor = .green
return v
}()
let bottomLabel: UILabel = {
let v = UILabel()
v.font = UIFont.systemFont(ofSize: 14.0)
v.numberOfLines = 0
v.backgroundColor = .cyan
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
[theScrollView, contentView, stackView, topLabel, centerLabel, bottomLabel].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
view.addSubview(theScrollView)
theScrollView.addSubview(contentView)
contentView.addSubview(stackView)
stackView.addArrangedSubview(topLabel)
stackView.addArrangedSubview(centerLabel)
stackView.addArrangedSubview(bottomLabel)
let contentViewHeightConstraint = contentView.heightAnchor.constraint(equalTo: theScrollView.heightAnchor, constant: 0.0)
contentViewHeightConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
// constrain all 4 sides of the scroll view to the safe area
theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0.0),
theScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0),
theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0),
// constrain all 4 sides of the content view to the scroll view
contentView.topAnchor.constraint(equalTo: theScrollView.topAnchor, constant: 0.0),
contentView.bottomAnchor.constraint(equalTo: theScrollView.bottomAnchor, constant: 0.0),
contentView.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor, constant: 0.0),
contentView.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor, constant: 0.0),
// constrain width of content view to width of scroll view
contentView.widthAnchor.constraint(equalTo: theScrollView.widthAnchor, constant: 0.0),
// constrain the stack view >= 8-pts from the top
// <= minus 8-pts from the bottom
// 40-pts leading and trailing
stackView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 8.0),
stackView.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -8.0),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 40.0),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -40.0),
// constrain stack view centerY to contentView centerY
stackView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 0.0),
// activate the contentView's height constraint
contentViewHeightConstraint,
])
topLabel.text = "Anger"
bottomLabel.text = "Based on information from Wikipedia APA Dictionary of Psychology"
// a sample paragraph of text
let centerSampleText = "Anger is an intense emotion defined as a response to a perceived provocation, the invasion of one’s boundaries, or a threat. From an evolutionary standpoint, anger servers to mobilise psychological resources in order to address the threat/invasion. Anger is directed at an individual of equal status."
// change to repeat the center-label sample text
let numberOfParagraphs = 2
var s = ""
for i in 1...numberOfParagraphs {
s += "\(i). " + centerSampleText
if i < numberOfParagraphs {
s += "\n\n"
}
}
centerLabel.text = s
}
}