I'm setting a new text value to a UILabel
. Currently, the new text appears just fine. However, I'd like to add some animation when the new text appears. I'm wondering what I can do to animate the appearance of the new text.
Here is the code to make this work.
[UIView beginAnimations:@"animateText" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:1.0f];
[self.lbl setAlpha:0];
[self.lbl setText:@"New Text";
[self.lbl setAlpha:1];
[UIView commitAnimations];
I wonder if it works, and it works perfectly!
Objective-C
[UIView transitionWithView:self.label
duration:0.25f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.label.text = rand() % 2 ? @"Nice nice!" : @"Well done!";
} completion:nil];
Swift 3, 4, 5
UIView.transition(with: label,
duration: 0.25,
options: .transitionCrossDissolve,
animations: { [weak self] in
self?.label.text = (arc4random()() % 2 == 0) ? "One" : "Two"
}, completion: nil)
transition
API as noted in the sample code, not the animate
API, which has almost the same signature. –
Octogenarian Objective-C
To achieve a true cross-dissolve transition (old label fading out while new label fading in), you don't want fade to invisible. It would result in unwanted flicker even if text is unchanged.
Use this approach instead:
CATransition *animation = [CATransition animation];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.type = kCATransitionFade;
animation.duration = 0.75;
[aLabel.layer addAnimation:animation forKey:@"kCATransitionFade"];
// This will fade:
aLabel.text = "New"
Also see: Animate UILabel text between two numbers?
Demonstration in iOS 10, 9, 8:
Tested with Xcode 8.2.1 & 7.1, ObjectiveC on iOS 10 to 8.0.
► To download the full project, search for SO-3073520 in Swift Recipes.
-addAnimation:forKey
to that label, then change the label's text. –
Anetta Swift 4
The proper way to fade a UILabel (or any UIView for that matter) is to use a Core Animation Transition
. This will not flicker, nor will it fade to black if the content is unchanged.
A portable and clean solution is to use a Extension
in Swift (invoke prior changing visible elements)
// Usage: insert view.fadeTransition right before changing content
extension UIView {
func fadeTransition(_ duration:CFTimeInterval) {
let animation = CATransition()
animation.timingFunction = CAMediaTimingFunction(name:
CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType.fade
animation.duration = duration
layer.add(animation, forKey: CATransitionType.fade.rawValue)
}
}
Invocation looks like this:
// This will fade
aLabel.fadeTransition(0.4)
aLabel.text = "text"
► Find this solution on GitHub and additional details on Swift Recipes.
MIT
license guarantees your Cocoapod can be used freely by everyone and anyone. –
Anetta since iOS4 it can be obviously done with blocks:
[UIView animateWithDuration:1.0
animations:^{
label.alpha = 0.0f;
label.text = newText;
label.alpha = 1.0f;
}];
Here is the code to make this work.
[UIView beginAnimations:@"animateText" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:1.0f];
[self.lbl setAlpha:0];
[self.lbl setText:@"New Text";
[self.lbl setAlpha:1];
[UIView commitAnimations];
With Swift 5, you can choose one of the two following Playground code samples in order to animate your UILabel
's text changes with some cross dissolve animation.
#1. Using UIView
's transition(with:duration:options:animations:completion:)
class method
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Car"
view.backgroundColor = .white
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toggle(_:)))
view.addGestureRecognizer(tapGesture)
}
@objc func toggle(_ sender: UITapGestureRecognizer) {
let animation = {
self.label.text = self.label.text == "Car" ? "Plane" : "Car"
}
UIView.transition(with: label, duration: 2, options: .transitionCrossDissolve, animations: animation, completion: nil)
}
}
let controller = ViewController()
PlaygroundPage.current.liveView = controller
#2. Using CATransition
and CALayer
's add(_:forKey:)
method
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let label = UILabel()
let animation = CATransition()
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Car"
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
// animation.type = CATransitionType.fade // default is fade
animation.duration = 2
view.backgroundColor = .white
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(toggle(_:)))
view.addGestureRecognizer(tapGesture)
}
@objc func toggle(_ sender: UITapGestureRecognizer) {
label.layer.add(animation, forKey: nil) // The special key kCATransition is automatically used for transition animations
label.text = label.text == "Car" ? "Plane" : "Car"
}
}
let controller = ViewController()
PlaygroundPage.current.liveView = controller
Swift 4.2 version of SwiftArchitect's solution above (works great):
// Usage: insert view.fadeTransition right before changing content
extension UIView {
func fadeTransition(_ duration:CFTimeInterval) {
let animation = CATransition()
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType.fade
animation.duration = duration
layer.add(animation, forKey: CATransitionType.fade.rawValue)
}
}
Invocation:
// This will fade
aLabel.fadeTransition(0.4)
aLabel.text = "text"
UILabel Extension Solution
extension UILabel{
func animation(typing value:String,duration: Double){
let characters = value.map { $0 }
var index = 0
Timer.scheduledTimer(withTimeInterval: duration, repeats: true, block: { [weak self] timer in
if index < value.count {
let char = characters[index]
self?.text! += "\(char)"
index += 1
} else {
timer.invalidate()
}
})
}
func textWithAnimation(text:String,duration:CFTimeInterval){
fadeTransition(duration)
self.text = text
}
//followed from @Chris and @winnie-ru
func fadeTransition(_ duration:CFTimeInterval) {
let animation = CATransition()
animation.timingFunction = CAMediaTimingFunction(name:
CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType.fade
animation.duration = duration
layer.add(animation, forKey: CATransitionType.fade.rawValue)
}
}
Simply Called function by
uiLabel.textWithAnimation(text: "text you want to replace", duration: 0.2)
Thanks for all the tips guys. Hope this will help in long term
Swift 2.0:
UIView.transitionWithView(self.view, duration: 1.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.sampleLabel.text = "Animation Fade1"
}, completion: { (finished: Bool) -> () in
self.sampleLabel.text = "Animation Fade - 34"
})
OR
UIView.animateWithDuration(0.2, animations: {
self.sampleLabel.alpha = 1
}, completion: {
(value: Bool) in
self.sampleLabel.alpha = 0.2
})
The animation's duration
and timingFunction
properties can be omitted, in which case they will take their default values of 0.25
and .curveEaseInEaseOut
, respectively.
let animation = CATransition()
label.layer.add(animation, forKey: nil)
label.text = "New text"
is the same as writing this:
let animation = CATransition()
animation.duration = 0.25
animation.timingFunction = .curveEaseInEaseOut
label.layer.add(animation, forKey: nil)
label.text = "New text"
Swift 4.2 solution (taking 4.0 answer and updating for new enums to compile)
extension UIView {
func fadeTransition(_ duration:CFTimeInterval) {
let animation = CATransition()
animation.timingFunction = CAMediaTimingFunction(name:
CAMediaTimingFunctionName.easeInEaseOut)
animation.type = CATransitionType.fade
animation.duration = duration
layer.add(animation, forKey: CATransitionType.fade.rawValue)
}
}
func updateLabel() {
myLabel.fadeTransition(0.4)
myLabel.text = "Hello World"
}
There is one more solution to achieve this. It was described here. The idea is subclassing UILabel
and overriding action(for:forKey:)
function in the following way:
class LabelWithAnimatedText: UILabel {
override var text: String? {
didSet {
self.layer.setValue(self.text, forKey: "text")
}
}
override func action(for layer: CALayer, forKey event: String) -> CAAction? {
if event == "text" {
if let action = self.action(for: layer, forKey: "backgroundColor") as? CAAnimation {
let transition = CATransition()
transition.type = kCATransitionFade
//CAMediatiming attributes
transition.beginTime = action.beginTime
transition.duration = action.duration
transition.speed = action.speed
transition.timeOffset = action.timeOffset
transition.repeatCount = action.repeatCount
transition.repeatDuration = action.repeatDuration
transition.autoreverses = action.autoreverses
transition.fillMode = action.fillMode
//CAAnimation attributes
transition.timingFunction = action.timingFunction
transition.delegate = action.delegate
return transition
}
}
return super.action(for: layer, forKey: event)
}
}
Usage examples:
// do not forget to set the "Custom Class" IB-property to "LabelWithAnimatedText"
// @IBOutlet weak var myLabel: LabelWithAnimatedText!
// ...
UIView.animate(withDuration: 0.5) {
myLabel.text = "I am animated!"
}
myLabel.text = "I am not animated!"
This is a C# UIView extension method that's based on @SwiftArchitect's code. When auto layout is involved and controls need to move depending on the label's text, this calling code uses the Superview of the label as the transition view instead of the label itself. I added a lambda expression for the action to make it more encapsulated.
public static void FadeTransition( this UIView AView, double ADuration, Action AAction )
{
CATransition transition = new CATransition();
transition.Duration = ADuration;
transition.TimingFunction = CAMediaTimingFunction.FromName( CAMediaTimingFunction.Linear );
transition.Type = CATransition.TransitionFade;
AView.Layer.AddAnimation( transition, transition.Type );
AAction();
}
Calling code:
labelSuperview.FadeTransition( 0.5d, () =>
{
if ( condition )
label.Text = "Value 1";
else
label.Text = "Value 2";
} );
If you would like to do this in Swift
with a delay try this:
delay(1.0) {
UIView.transitionWithView(self.introLabel, duration: 0.25, options: [.TransitionCrossDissolve], animations: {
self.yourLabel.text = "2"
}, completion: { finished in
self.delay(1.0) {
UIView.transitionWithView(self.introLabel, duration: 0.25, options: [.TransitionCrossDissolve], animations: {
self.yourLabel.text = "1"
}, completion: { finished in
})
}
})
}
using the following function created by @matt - https://mcmap.net/q/64374/-dispatch_after-gcd-in-swift:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
which will become this in Swift 3
func delay(_ delay:Double, closure:()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.after(when: when, execute: closure)
}
© 2022 - 2024 — McMap. All rights reserved.