I’m facing this weird animation issues when hiding UIButton
in a StackView
using the new iOS 15 Configuration
. See playground:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
private weak var contentStackView: UIStackView!
override func viewDidLoad() {
view.frame = CGRect(x: 0, y: 0, width: 300, height: 150)
view.backgroundColor = .white
let contentStackView = UIStackView()
contentStackView.spacing = 8
contentStackView.axis = .vertical
for _ in 1...2 {
contentStackView.addArrangedSubview(makeConfigurationButton())
}
let button = UIButton(type: .system)
button.setTitle("Toggle", for: .normal)
button.addAction(buttonAction, for: .primaryActionTriggered)
view.addSubview(contentStackView)
view.addSubview(button)
contentStackView.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentStackView.topAnchor.constraint(equalTo: view.topAnchor),
contentStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
self.contentStackView = contentStackView
}
private var buttonAction: UIAction {
UIAction { [weak self] _ in
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 1, delay: 0) {
guard let toggleElement = self?.contentStackView.arrangedSubviews[0] else { return }
toggleElement.isHidden.toggle()
toggleElement.alpha = toggleElement.isHidden ? 0 : 1
self?.contentStackView.layoutIfNeeded()
}
}
}
private func makeSystemButton() -> UIButton {
let button = UIButton(type: .system)
button.setTitle("System Button", for: .normal)
return button
}
private func makeConfigurationButton() -> UIButton {
let button = UIButton()
var config = UIButton.Configuration.filled()
config.title = "Configuration Button"
button.configuration = config
return button
}
}
PlaygroundPage.current.liveView = MyViewController()
Which results in this animation:
But I want the animation to look like this, where the button only shrinks vertically:
Which you can replicate in the playground by just swapping contentStackView.addArrangedSubview(makeConfigurationButton())
for contentStackView.addArrangedSubview(makeSystemButton())
.
I guess this has something to do with the stack view alignment
, setting it to center
gives me the desired animation, but then the buttons don’t fill the stack view width anymore and setting the width through AutoLayout results in the same animation again… Also, having just one system button in the stack view results in the same weird animation, but why does it behave differently for two system buttons? What would be a good solution for this problem?
2
Answers
You should add height constraint to buttons and update this constraint while animating. I edit your code just as below.
As you’ve seen, the built-in show/hide animation with
UIStackView
can be quirky (lots of other quirks when you really get into it).It appears that, when using a button with
UIButton.Configuration
, the button’s width changes from the width assigned by the stack view to its intrinsic width as the animation occurs.We can get around that by giving the button an explicit height constraint — but, what if we want to use the intrinsic height (which may not be known in advance)?
Instead of setting the constraint, set the button’s Content Compression Resistance Priority::
And we no longer get the horizontal sizing:
As you will notice, though, the button doesn’t "squeeze" vertically… it gets "pushed up" outside the stack view’s bounds.
We can avoid that by setting
.clipsToBounds = true
on the stack view:If this effect is satisfactory, we’re all set.
However, as we can see, the button is still not getting "squeezed." If that is the visual effect we want, we can use a custom "self-stylized" button instead of a Configuration button:
Of course, there is very little visual difference – and looking closely the button’s text is not squeezing. If we really, really, really want that to happen, we need to animate a transform instead of using the stack view’s default animation.
And… if we are taking advantage of some of the other conveniences with Configurations, using a self-stylized
UIButton
might not be an option.If you want to play with the differences, here’s some sample code:
Looks like this:
Edit
Quick example of another "quirk" when it comes to hiding a stack view’s arranged subview (excess code in here, but I stripped down the above example):
When this is run and the "Toggle" button is tapped, it will be painfully obvious what’s "not-quite-right."