Letter by letter animation for UILabel?

18,916

Solution 1

Update for 2018, Swift 4.1:

extension UILabel {

    func animate(newText: String, characterDelay: TimeInterval) {

        DispatchQueue.main.async {

            self.text = ""

            for (index, character) in newText.enumerated() {
                DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
                    self.text?.append(character)
                }
            }
        }
    }

}

calling it is simple and thread safe:

myLabel.animate(newText: myLabel.text ?? "May the source be with you", characterDelay: 0.3)

@objC, 2012:

Try this prototype function:

- (void)animateLabelShowText:(NSString*)newText characterDelay:(NSTimeInterval)delay
{    
    [self.myLabel setText:@""];

    for (int i=0; i<newText.length; i++)
    {
        dispatch_async(dispatch_get_main_queue(),
        ^{
            [self.myLabel setText:[NSString stringWithFormat:@"%@%C", self.myLabel.text, [newText characterAtIndex:i]]];
        });

        [NSThread sleepForTimeInterval:delay];
    }
}

and call it in this fashion:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
    [self animateLabelShowText:@"Hello Vignesh Kumar!" characterDelay:0.5];
});

Solution 2

Here's @Andrei G.'s answer as a Swift extension:

extension UILabel {

    func setTextWithTypeAnimation(typedText: String, characterInterval: NSTimeInterval = 0.25) {
        text = ""
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)) {
            for character in typedText.characters {
                dispatch_async(dispatch_get_main_queue()) {
                    self.text = self.text! + String(character)
                }
                NSThread.sleepForTimeInterval(characterInterval)
            }
        }
    }

}

Solution 3

This might be better.

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *string =@"Risa Kasumi & Yuma Asami";

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:string forKey:@"string"];
    [dict setObject:@0 forKey:@"currentCount"];
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(typingLabel:) userInfo:dict repeats:YES];
    [timer fire];


}

-(void)typingLabel:(NSTimer*)theTimer
{
    NSString *theString = [theTimer.userInfo objectForKey:@"string"];
    int currentCount = [[theTimer.userInfo objectForKey:@"currentCount"] intValue];
    currentCount ++;
    NSLog(@"%@", [theString substringToIndex:currentCount]);

    [theTimer.userInfo setObject:[NSNumber numberWithInt:currentCount] forKey:@"currentCount"];

     if (currentCount > theString.length-1) {
        [theTimer invalidate];
    }

    [self.label setText:[theString substringToIndex:currentCount]];
}

Solution 4

I have write a demo , you can use it , it support ios 3.2 and above

in your .m file

- (void)displayLabelText
{

    i--;
    if(i<0)
    {
        [timer invalidate];
    }
    else
    {
        [label setText:[NSString stringWithFormat:@"%@",[text substringToIndex:(text.length-i-1)]]];
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 60)];
    [label setBackgroundColor:[UIColor redColor]];
    text = @"12345678";
    [label setText:text];
    [self.view addSubview:label];
    i=label.text.length;
    timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(displayLabelText) userInfo:nil repeats:YES];
    [timer fire];    
}

in your .h file

@interface labeltextTestViewController : UIViewController {
    UILabel *label;
    NSTimer *timer;
    NSInteger i;
    NSString *text;
}

with the demo , i think you can do in your situation , with a little change the code look like very very ugly because i have to go to have dinner, you can majorization it.

Solution 5

Swift 3 ,Still credit on Andrei G. concept.

extension UILabel{

func setTextWithTypeAnimation(typedText: String, characterInterval: TimeInterval = 0.25) {
    text = ""
    DispatchQueue.global(qos: .userInteractive).async {

        for character in typedText.characters {
            DispatchQueue.main.async {
                self.text = self.text! + String(character)
            }
            Thread.sleep(forTimeInterval: characterInterval)
        }

    }
}

}
Share:
18,916
vignesh kumar
Author by

vignesh kumar

Aspiring Developer to reach millions with my work thus making my life meaningful #SOreadytohelp

Updated on June 02, 2022

Comments

  • vignesh kumar
    vignesh kumar almost 2 years

    Is there a way to animate the text displayed by UILabel. I want it to show the text value character by character.

    Help me with this folks

  • Luke
    Luke over 8 years
    The problem with this approach is if you begin writing a word at the end of one line, and it then needs to shift to a second line (i.e. if you add enough characters). This will cause half of the word to appear on the first line, then the word to jerk down to the next line once it realises.
  • Roman Safin
    Roman Safin over 8 years
    Correct me if i'm not right, this approach uses CPU for animation. But as far as i know this one is not recommended way. Is anyone know Core Animation way(Which basically use GPU for animation)?
  • Jayprakash Dubey
    Jayprakash Dubey almost 8 years
    I want springDampning effect that is available in UIButton. How can we acheive this in UILabel?
  • lucius degeer
    lucius degeer over 6 years
    Thanks for this great answer. I'm curious how to avoid garbled text when using this async effect in a tableview that reloads often? I have tried using NSLock () and a separate DispatchQueue for the text animations but I still get multiple strings asynchronously mixing into garbled text. Thanks for any help you can provide.