Reputation: 1098
I'm looking to build a view which will grow as subviews are added to it. At first the height will be zero, then should grow as I add subviews until it reaches a maximum size, and then I want it to become scrollable.
If I build this using a UIView inside a UIScrollView and set up the constraints from top to bottom manually, it works as expected. However, I feel like this should be possible using a UIStackView, but when I try either the scroll view doesn't grow, or the contentSize of the scrollView gets stuck at the maximum height, and won't scroll. I've been playing with this for a while and think it might actually be a bug with UIStackView and Autolayout, but hopefully I just missed something. Wrapping the UIStackView in a container UIView doesn't help.
Here's a complete view controller which shows the problem (tapping anywhere adds labels to the scroll view)
import UIKit
class DumbScrollStack: UIViewController {
let stack = UIStackView()
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
//Setup
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(addLabel)))
stack.axis = .vertical
view.backgroundColor = .blue
scrollView.backgroundColor = .red
//Add scroll view and setup position
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
//Set maximum height
scrollView.heightAnchor.constraint(lessThanOrEqualToConstant: 100).isActive = true
//add stack
scrollView.addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
//setup scrollView content size constraints
stack.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stack.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
stack.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
stack.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
// Keep short if stack height is lower than scroll height
// With this constraint the scrollView grows as expected, but doesn't scroll when it goes over 100px
// Without this constraint the scrollView is always 100px tall but it does scroll
let constraint = scrollView.heightAnchor.constraint(equalTo: stack.heightAnchor)
constraint.priority = UILayoutPriority(rawValue: 999)
constraint.isActive = true
addLabel()
}
@objc func addLabel() {
let label = UILabel()
label.backgroundColor = .gray
label.text = "Hello"
stack.addArrangedSubview(label)
}
}
Upvotes: 7
Views: 1587
Reputation: 100541
Add this
label.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .vertical)
or comment this block
let constraint = scrollView.heightAnchor.constraint(equalTo: stack.heightAnchor)
constraint.priority = UILayoutPriority(rawValue: 999)
constraint.isActive = true
or make this lower than 750 ( the label's default vertical compression )
constraint.priority = UILayoutPriority(rawValue: 749)
the problem is that the label's vertical compression priority is lower than 999 which always make the stack equal to the scrollview height , hence no scroll
Upvotes: 9
Reputation: 810
Have you looked into using a UICollectionView? It sounds like what you're trying to rebuild.
Make sure to set "Scrolling Enabled" to true and set the scroll direction.
Upvotes: 1