I have a UIScrollView
with a vertical stack view, which has a label and a button, I want the button to stay at bottom of the screen, and as soon as label's text increases and reaches the button, the button would stay below the label and they scroll together.
I'm thinking of:
let lableFit = self.label.systemLayoutSizeFitting(targetSize)
let buttonFit = self.button.systemLayoutSizeFitting(targetSize)
if labelFit.height + buttonFit.height > contentHeight {
button.bottomAnchor.constrain(equalTo:scrollView.contentLayoutGuide.bottomAnchor)
} else {
button.bottomAnchor.constrain(equalTo:scrollView.frameLayoutGuide.bottomAnchor)
}
Could this work? And what should be the target size for fitting label and button?
Thanks!
It sounds like you want this:
We're going to set it up like this:
We need to configure the stack view to allow a variable amount of empty space between the label and the button. We can do that by setting the stack view's “Distribution” to “Equal Spacing”. Then set the stack view's “Spacing” to the minimum amount of padding you want between the label and the button. I set it to 12 for my demo.
Once we have that view hierarchy, we need to create the appropriate constraints. Let's restate your goal in a different way: we want the stack view (which contains the label and the button) to be at least as tall as the screen. What constraints do we need?
A scroll view always needs constraints between all four of its edges and its children, because those constraints tell the scroll view how to set its contentSize
. In this case, we can constrain the four edges of the scroll view to the corresponding edges of the stack view. These are the “Stack View.xxx = xxx” and “xxx = Stack View.xxx” constraints in the screen shot.
We also need to tell the stack view how big to be. We can't do this with constraints between the stack view and the scroll view, because those only affect the scroll view's contentSize
. Instead, we constraint the stack view to the root view.
We want the stack view to be as wide as the screen, so we constrain the stack view's width to equal the root view safe area's width. This is the “Stack View.width = Safe Area.width” constraint.
We want the stack view to be at least as tall as the screen, but we want to allow it to be taller, so we constrain the stack view's height to be greater than or equal to the root view safe area's height. This is the “Stack View.height ≥ Safe Area.height” constraint.
We want the scroll view to fill the screen, so we constrain its edges to the edges of the root view's safe area. These are the “Safe Area.xxx = Scroll View.xxx” constraints in the screen shot.
To ensure that the label wraps its text appropriately, constrain the width of the label to equal the width of the stack view. This is the “Label.width = width” constraint in the screen shot.
You should also set the content size priorities of both the label and the button as follows:
Content Hugging Priority
Horizontal: 800
Vertical: 800
Content Compression Resistance Priority
Horizontal: 1000
Vertical: 1000
This will ensure that neither the label nor the button is stretched or squished inappropriately.
For the demo, I hooked my storyboard up to this view controller:
import UIKit
class ViewController: UIViewController {
@IBOutlet var label: UILabel!
@IBOutlet var scrollView: UIScrollView!
@IBAction func buttonWasTapped() {
label.text = (label.text ?? "") + "\n\nhere is\nmore text\nfor demo\npurposes"
scrollView.layoutIfNeeded()
scrollView.scrollRectToVisible(CGRect(x: 0, y: scrollView.contentSize.height - 1, width: 1, height: 1), animated: true)
}
}