The .fillProportionally property of UIStackView is (from my experience) one of the most misunderstood elements of auto-layout.
So, I'm not entirely sure this is going to give you what you want, but give it a try.

Tapping the Text button will change the "description" text and tapping the Height button will change the height of the "container" view, so you can see how it looks with different amounts of text.
The Report button will print the resulting heights and proportional ratios of the views.
All via code - no @IBOutlet or @IBAction connections - so just start with a new view controller and assign its custom class to ProportionalStackViewController:
class ProportionalHeightView: UIView {
let myNonScrollTextView: UITextView = {
let v = UITextView()
v.isScrollEnabled = false
v.setContentHuggingPriority(.required, for: .vertical)
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
let padding: CGFloat = 0.0
addSubview(myNonScrollTextView)
myNonScrollTextView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myNonScrollTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
myNonScrollTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
// if we want the text top-aligned
//myNonScrollTextView.topAnchor.constraint(equalTo: topAnchor),
// if we want the text vertically=sentered
myNonScrollTextView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
myNonScrollTextView.sizeToFit()
invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return myNonScrollTextView.bounds.size
}
}
class TitleView: ProportionalHeightView {
override func commonInit() {
super.commonInit()
myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
myNonScrollTextView.backgroundColor = .cyan
backgroundColor = .blue
}
}
class DescView: ProportionalHeightView {
override func commonInit() {
super.commonInit()
myNonScrollTextView.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
myNonScrollTextView.backgroundColor = .yellow
backgroundColor = .orange
}
}
class ProportionalStackViewController: UIViewController {
var titleView: ProportionalHeightView = {
let v = ProportionalHeightView()
v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 22.0, weight: .bold)
v.myNonScrollTextView.backgroundColor = .cyan
v.backgroundColor = .blue
return v
}()
var descView: ProportionalHeightView = {
let v = ProportionalHeightView()
v.myNonScrollTextView.font = UIFont.systemFont(ofSize: 16.0, weight: .regular)
v.myNonScrollTextView.backgroundColor = .yellow
v.backgroundColor = .orange
return v
}()
let containerView: UIView = {
let v = UIView()
v.backgroundColor = .white
return v
}()
let proportionalStackView: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.distribution = .fillProportionally
return v
}()
let changeTextButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Text", for: .normal)
return b
}()
let changeHeightButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Height", for: .normal)
return b
}()
let reportButton: UIButton = {
let b = UIButton()
b.backgroundColor = .gray
b.setTitle("Report", for: .normal)
return b
}()
let btnStack: UIStackView = {
let v = UIStackView()
v.distribution = .fillEqually
v.spacing = 20
return v
}()
var nLines = 0
var containerH = NSLayoutConstraint()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemTeal
btnStack.translatesAutoresizingMaskIntoConstraints = false
proportionalStackView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
// add a horizontal stack view with buttons at the top
btnStack.addArrangedSubview(changeTextButton)
btnStack.addArrangedSubview(changeHeightButton)
btnStack.addArrangedSubview(reportButton)
view.addSubview(btnStack)
// set text for titleView
titleView.myNonScrollTextView.text = "Pleasanton Panthers"
descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"
proportionalStackView.addArrangedSubview(titleView)
proportionalStackView.addArrangedSubview(descView)
containerView.addSubview(proportionalStackView)
view.addSubview(containerView)
// respect safe area
let g = view.safeAreaLayoutGuide
containerH = containerView.heightAnchor.constraint(equalToConstant: 240.0)
NSLayoutConstraint.activate([
// buttons stack 20-pts from top / leading / trailing
btnStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btnStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btnStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// container view 20-pts from bottom of buttons, 20-pts from leading / trailing
containerView.topAnchor.constraint(equalTo: btnStack.bottomAnchor, constant: 20.0),
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// container view height
containerH,
// constrain stack view 20-pts from top/bottom/leading/trailing to container
proportionalStackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
proportionalStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),
proportionalStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
proportionalStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),
])
changeTextButton.addTarget(self, action: #selector(changeText), for: .touchUpInside)
changeHeightButton.addTarget(self, action: #selector(changeHeight), for: .touchUpInside)
reportButton.addTarget(self, action: #selector(report), for: .touchUpInside)
[titleView, titleView.myNonScrollTextView, descView, descView.myNonScrollTextView].forEach {
v in
// un-comment next line to clear background colors
//v.backgroundColor = .clear
}
}
@objc func report() -> Void {
let titleTextH = titleView.myNonScrollTextView.frame.size.height
let descTextH = descView.myNonScrollTextView.frame.size.height
let titleViewH = titleView.frame.size.height
let descViewH = descView.frame.size.height
let tRatio = titleTextH / descTextH
let vRatio = titleViewH / descViewH
print("text heights:\t", titleTextH, descTextH)
print("view heights:\t", titleViewH, descViewH)
print("Text view ratio: \(tRatio) view ratio: \(vRatio)")
}
@objc func changeHeight() -> Void {
if containerView.frame.origin.y + containerView.frame.size.height > view.frame.size.height - 20 {
containerH.constant = 220
}
containerH.constant += 20
}
@objc func changeText() -> Void {
nLines += 1
if nLines > 10 {
descView.myNonScrollTextView.text = "A one stop destination for all the Panthers fans! Experience our new futuristic techology-enabled fan experience an much more!" // "Single line"
nLines = 0
return
}
var s = ""
for i in 1..<nLines {
s += "Line \(i)\n"
}
s += "Line \(nLines)"
descView.myNonScrollTextView.text = s
}
}
textView.contentSizeinstead oftextView.intrinsicContentSize? Okay, I'm being lazy and not trying it myself, sorry, but could you just check? - mattcustom view 1aUIViewwith a label? Andcustom view 2is aUIViewwith a (non-scrolling) text view? Based on that, how do you want your proportion calculated? It's still not clear (from your updated post) how the end result should look. - DonMag