Animating text changes in UILabel

I am setting a new text value in UILabel . Currently, the new text looks just fine. However, I want to add animation when new text appears. I am wondering what I can do to bring the new text to life.

+107
ios objective-c uilabel ipad caanimation
Jun 18 '10 at 22:44
source share
14 answers

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]; 
+16
Jun 18 '10 at 23:28
source share

Objective-c

To achieve the cross-dissolution of the true (the old mark disappears while the new mark disappears), you do not want the invisible to disappear. This will cause unwanted flickering even if the text does not change.

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?

Demo in iOS 10, 9, 8:

Empty, then transition from transition 1 to 5




Tested with Xcode 8.2.1 and 7.1 , ObjectiveC on iOS 10 to 8.0 .

β–Ί To download the complete project, search for SO-3073520 in Quick Recipes .

+151
May 03 '13 at 20:55
source share

I wonder if it works and if 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

 UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: { [weak self] in self?.label.text = (arc4random()() % 2 == 0) ? "One" : "Two" }, completion: nil) 
+149
Sep 17 '14 at 12:43 on
source share

Swift 4

The correct way to disappear UILabel (or any UIView for that matter) is to use Core Animation Transition . It will not flicker, and will not fade in black if the contents have not changed.

A portable and clean solution is to use Extension in Swift (invoke previously modified 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) } } 

The call looks like this:

 // This will fade aLabel.fadeTransition(0.4) aLabel.text = "text" 

Blank, then 1 to 5 fade transition




β–Ί Find this solution on GitHub and more information on Swift Recipes .

+107
Dec 25 '14 at 8:26
source share

since iOS4 can be explicitly done using blocks:

 [UIView animateWithDuration:1.0 animations:^{ label.alpha = 0.0f; label.text = newText; label.alpha = 1.0f; }]; 
+20
Jun 28 '12 at 19:36
source share

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 }) 
+3
Feb 11 '16 at 9:00
source share

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) } } 

Function is just called

 uiLabel.textWithAnimation(text: "text you want to replace", duration: 0.2) 

Thanks for all the advice guys. Hope this helps in the long run

+2
Feb 21 '19 at 17:08
source share

This is a C # UIView extension method based on @SwiftArchitect code. When auto-layout is enabled and controls must move based on the label text, this calling code uses Superview tags as a transition instead of the tag 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(); } 

Call Code:

  labelSuperview.FadeTransition( 0.5d, () => { if ( condition ) label.Text = "Value 1"; else label.Text = "Value 2"; } ); 
+1
Aug 09 '16 at 4:58
source share

Swift 4.2 version of SwiftArchitect 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) } } 

Vocation:

  // This will fade aLabel.fadeTransition(0.4) aLabel.text = "text" 
+1
26 Sep '18 at 10:50
source share

Swift 4.2 solution (getting a 4.0 response and updating for new enumerations 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" } 
+1
Jan 25 '19 at 15:01
source share

If you want 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 - stack overflow.site/questions/15746 / ... :

 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 the same in Swift 3

 func delay(_ delay:Double, closure:()->()) { let when = DispatchTime.now() + delay DispatchQueue.main.after(when: when, execute: closure) } 
0
Jun 29 '16 at 17:40
source share

In Swift 5, you can choose one of the following two Playground code examples to animate a UILabel text using a cross-dissolve animation.




# 1. Using transition(with:duration:options:animations:completion:) UIView 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 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 
0
Dec 11 '16 at 23:42 on
source share

There is another solution to achieve this. It has been described here . The idea is to UILabel subclass of UILabel and override the action(for:forKey:) function as follows:

 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) } } 

Examples of using:

 // 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!" 
0
Jan 28 '19 at 12:25
source share

System defaults of 0.25 for duration and .curveEaseInEaseOut for timingFunction are often preferable for consistency of animations and can be omitted:

 let animation = CATransition() label.layer.add(animation, forKey: nil) label.text = "New text" 

which 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" 
0
Apr 7 '19 at 15:17
source share



All Articles