Modern UIButton
configuration API allows for new way of setting button appearance. However the code below skips the animation entirely.
@IBAction func buttonTouched(_ sender: UIButton) {
sender.configuration?.background.backgroundColor = .green
UIView.animate(withDuration: 0.4, delay: 0.5) {
sender.configuration?.background.backgroundColor = .systemMint
}
}
Similar thing can be done like this:
@IBAction func buttonTouched(_ sender: UIButton) {
sender.backgroundColor = .red
UIView.animate(withDuration: 0.4, delay: 0.5) {
sender.backgroundColor = .systemMint
}
}
And this works. The question is how to animate UIButton
's configuration changes.
CodePudding user response:
After some quick searching, it appears configuration.background.backgroundColor
is not animatable.
Depending on your needs, you can use a .customView
for the button's background and then animate the color change for that view.
Quick example (assuming you've added the button as an @IBOutlet
):
class TestVC: UIViewController {
@IBOutlet var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let v = UIView()
v.backgroundColor = .red
button.configuration?.background.customView = v
}
@IBAction func buttonTouched(_ sender: UIButton) {
if let v = sender.configuration?.background.customView {
UIView.animate(withDuration: 0.4, delay: 0.5) {
v.backgroundColor = v.backgroundColor == .red ? .systemMint : .red
}
}
}
}
CodePudding user response:
The way to achieve animated configuration change by using transitions is the only workaround I could find so far. Somewhat like this:
@IBAction func buttonTouched(_ sender: UIButton) {
UIView.transition(with: sender, duration: 0.3, options: .transitionCrossDissolve) {
sender.configuration?.background.backgroundColor = .red
} completion: { _ in
UIView.transition(with: sender, duration: 1.0, options: .transitionCrossDissolve) {
sender.configuration?.background.backgroundColor = .cyan
}
}
}
When I tried to optimize and reuse code, I ended with something like:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Another option is subclass `UIButton` and override its `configurationUpdateHandler`
view.subviews.compactMap({ $0 as? UIButton }).forEach {
$0.configurationUpdateHandler = { button in
guard let config = button.configuration else { return }
button.setConfiguration(config,duration: 2)
}
}
}
@IBAction func buttonTouched(_ sender: UIButton) {
// sender.configuration?.background.backgroundColor = .red
// sender.configuration?.baseForegroundColor = .white
// The above would work, but we should rather aggregate all changes at once.
if var config = sender.configuration {
config.background.backgroundColor = .red
config.baseForegroundColor = .white
sender.configuration = config
}
}
}
extension UIButton {
func setConfiguration(_ configuration: UIButton.Configuration, duration: Double = 0.25, completion: ((Bool) -> Void)? = nil) {
UIView.transition(with: self, duration: duration, options: .transitionCrossDissolve) {
self.configuration? = configuration
} completion: { completion?($0) }
}
}
I