Reputation: 1958
I have a UIButton
, and I would like to change its text by accessing its titleLabel
attribute. However, the way I want the button to change text is by first shrinking down to a very tiny size, then changing instantly while it's invisible, then scaling back up. After looking through multiple posts on here, I have reached this:
let changeText = CATransition();
changeText.type = kCATransitionReveal;
changeText.duration = 0.0;
changeText.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionLinear);
submitButton.titleLabel?.layer.add(changeText, forKey: "changeTextTransition");
UIView.animateKeyframes(withDuration: 0.6, delay: 0, options: .calculationModeLinear, animations: {
//Zzzeeeewwwwwwwwww
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5, animations: {
self.submitButton.titleLabel?.transform = self.submitButton.titleLabel!.transform.scaledBy(x: 0.001, y: 0.001);
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.0, animations: {
self.submitButton.titleLabel?.text = "Green";
})
//Wwwwwweeeeeeyyyyyppp
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
self.submitButton.titleLabel?.transform = self.submitButton.titleLabel!.transform.scaledBy(x: 1000, y: 1000);
})
}, completion: nil)
The problem is that this doesn't work. What I get is a quick flash of the words "New Text" as the label shrinks, and then when it scales back up, it's still "Old Text". It's so weird I can't even begin to wrap my head around what might be the cause. I think what's happening is that it plays the reveal transition on the new text before it even shrinks down (No idea why since I specified duration=0), then grows back the old text.
Here is what it looks like (with some background color change that I omitted above):
Upvotes: 1
Views: 1028
Reputation: 119242
First off, congratulations for including sound effects in your code comments, glad it's not just me that does that.
The text property of a label cannot be animated, so it is applied immediately, spoiling your animation.
One solution, which I use a lot for complex animations, is to use snapshots. At the start of the animation, create a snapshot of the label, then set the alpha of the label to zero and update the text. Then, animate the transforms of the label and snapshot together, when they're small, make the label visible and the snapshot invisible, then animate back up.
That sounds more complicated than it is, here's code to run it in a playground:
import UIKit
import PlaygroundSupport
let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
view.backgroundColor = .blue
PlaygroundPage.current.liveView = view
let label = UILabel(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
label.text = "Hello"
label.font = UIFont.systemFont(ofSize: 50)
label.textAlignment = .center
label.textColor = .white
view.addSubview(label)
let snapshot = label.snapshotView(afterScreenUpdates: true)!
view.addSubview(snapshot)
snapshot.frame = label.frame
label.alpha = 0
label.text = "Goodbye"
UIView.animateKeyframes(withDuration: 2, delay: 0, options: [.repeat, .autoreverse], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.45) {
label.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
snapshot.transform = label.transform
}
UIView.addKeyframe(withRelativeStartTime: 0.45, relativeDuration: 0.1) {
label.alpha = 1.0
snapshot.alpha = 0.0
}
UIView.addKeyframe(withRelativeStartTime: 0.55, relativeDuration: 0.45) {
label.transform = CGAffineTransform.identity
}
}) { _ in
snapshot.removeFromSuperview()
}
Which gives you this result:
It's not clear to me what you are trying to do with the CATransition - unless there's another effect you're looking for as well, that isn't helping your cause.
Upvotes: 3