I have one UIStackView that stacks vertically two horizontal UIStackViews. That worked just fine. But the first sub-UIStackView had too many elements so I decided to wrap it inside a scrollView. The problem is that when I do so, this first sub-view just disappears, as if the UIScrollView had a height of 0.
Here's the code:
func initViews(){
let containerStack = UIStackView()
addSubview(containerStack)
containerStack.axis = .vertical
containerStack.distribution = .equalCentering
containerStack.isLayoutMarginsRelativeArrangement = true
containerStack.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
let scrollView = UIScrollView()
let scrolledStack = UIStackView()
containerStack.addArrangedSubview(scrollView)
scrollView.addSubview(scrolledStack)
scrolledStack.distribution = .fill
scrolledStack.isLayoutMarginsRelativeArrangement = true
scrolledStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 20, right: 30)
scrolledStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
scrolledStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
scrolledStack.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
scrolledStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
scrolledStack.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
let otherStack = UIStackView()
containerStack.addArrangedSubview(otherStack)
otherStack.distribution = .equalCentering
otherStack.isLayoutMarginsRelativeArrangement = true
otherStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 0, right: 30)
containerStack.translatesAutoresizingMaskIntoConstraints = false
containerStack.topAnchor.constraint(equalTo: topAnchor).isActive = true
containerStack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
containerStack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
containerStack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
let text = UITextView()
text.font = UIFont.boldSystemFont(ofSize: 14)
text.text = number
text.isScrollEnabled = false
text.backgroundColor = .clear
scrolledStack.addArrangedSubview(text)
}
for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
let text = UITextView()
text.font = UIFont.boldSystemFont(ofSize: 14)
text.text = number
text.isScrollEnabled = false
text.backgroundColor = .clear
otherStack.addArrangedSubview(text)
}
}
otherStack behaves as expected, but scrollView and scrolledStack just don't appear. If I remove the scrollView and just add scrolledStack to containerStack, both stacks appear as expected.
CodePudding user response:
Many UI view elements - such as UIScrollView
- have no intrinsic height.
You are putting a scroll view in a vertical stack view, with your stack view set to:
containerStack.distribution = .equalCentering
The problem comes in because the stack view cannot center its arranged subviews if they have no height.
If you change your containerStack
to:
containerStack.distribution = .fillEqually
You will see both of the arranged subviews.
If you don't want them to have equal heights, then you need to provide some height value... either with an explicit scrollView.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
or perhaps relative to the overall height.
Edit - in response to comment...
Add these lines at the bottom of your initViews()
func:
scrolledStack.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .red
scrolledStack.backgroundColor = .lightGray
otherStack.backgroundColor = .systemBlue
containerStack.backgroundColor = .systemGreen
and, instead of .clear
, give your first set of text views:
text.backgroundColor = .cyan
and your second set of text views:
text.backgroundColor = .green
Here's how it looks now when I run your code:
That is probably not what you're going for, but by giving your views contrasting background colors, it makes it much easier to see what's going on.
CodePudding user response:
I readapted a bit your code and created this swift playground:
import UIKit
import PlaygroundSupport
final class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let containerStack = UIStackView()
view.addSubview(containerStack)
containerStack.axis = .vertical
containerStack.distribution = .equalCentering
containerStack.translatesAutoresizingMaskIntoConstraints = false
let scrollView = UIScrollView()
let scrolledStack = UIStackView()
scrolledStack.translatesAutoresizingMaskIntoConstraints = false
containerStack.addArrangedSubview(scrollView)
scrollView.addSubview(scrolledStack)
NSLayoutConstraint.activate([
scrolledStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
scrolledStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
scrolledStack.topAnchor.constraint(equalTo: scrollView.topAnchor),
scrolledStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
scrolledStack.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor)
])
let otherStack = UIStackView()
containerStack.addArrangedSubview(otherStack)
otherStack.distribution = .equalCentering
otherStack.isLayoutMarginsRelativeArrangement = true
otherStack.layoutMargins = UIEdgeInsets(top: 20, left: 30, bottom: 0, right: 30)
NSLayoutConstraint.activate([
containerStack.topAnchor.constraint(equalTo: view.topAnchor),
containerStack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
containerStack.leftAnchor.constraint(equalTo: view.leftAnchor),
containerStack.rightAnchor.constraint(equalTo: view.rightAnchor),
])
for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
let text = UILabel()
text.font = UIFont.boldSystemFont(ofSize: 14)
text.text = number
scrolledStack.addArrangedSubview(text)
NSLayoutConstraint.activate([
text.widthAnchor.constraint(equalToConstant: 100)
])
}
for number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]{
let text = UILabel()
text.font = UIFont.boldSystemFont(ofSize: 14)
text.text = number
otherStack.addArrangedSubview(text)
}
}
}
PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true