Зміна анімаційного тексту в UILabel


133

Я встановлюю нове значення тексту на UILabel. Наразі новий текст виглядає просто чудово. Однак я хотів би додати деяку анімацію, коли з’явиться новий текст. Мені цікаво, що я можу зробити, щоб оживити появу нового тексту.


Для Swift 5 дивіться мою відповідь, яка показує два різні способи вирішення вашої проблеми.
Іману Петі

Відповіді:


177

Цікаво, чи працює вона, і це прекрасно працює!

Ціль-С

[UIView transitionWithView:self.label 
                  duration:0.25f 
                   options:UIViewAnimationOptionTransitionCrossDissolve 
                animations:^{

    self.label.text = rand() % 2 ? @"Nice nice!" : @"Well done!";

  } completion:nil];

Швидкий 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)

16
Зауважте, що TransitionCrossDissolve є ключовим для цього.
akaru

7
У блоках анімації UIView вам не потрібна [слабка власна особистість]. Див stackoverflow.com/a/27019834/511299
Sunkas

162

Ціль-С

Щоб досягти справжнього перехресного розчинення (стара мітка зникає, а нова мітка зникає), ви не хочете, щоб зникне невидиме. Це призведе до небажаного мерехтіння, навіть якщо текст не зміниться .

Використовуйте замість цього підхід:

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"

Також дивіться: Анімований текст UILabel між двома номерами?

Демонстрація в iOS 10, 9, 8:

Пустий, то від 1 до 5 згасання перехід


Тестовано з Xcode 8.2.1 та 7.1 , ObjectiveC на iOS 10 до 8.0 .

► Щоб завантажити повний проект, знайдіть SO-3073520 у розділі Рецепти Swift .


Коли я встановлюю дві мітки одночасно, це дає тремтіння на тренажері.
huggie

1
Можливо, неправильно використали? Сенс мого підходу - не використовувати 2 мітки. Ви використовуєте одну мітку -addAnimation:forKeyдля цієї мітки, а потім змінюєте текст мітки.
SwiftArchitect

@ConfusedVorlon, будь ласка, підтвердьте свою заяву. і перевірити, чи не розміщено проект на сайті swiftarchitect.com/recipes/#SO-3073520 .
SwiftArchitect

Я, можливо, раніше отримав неправильний кінець палиці. Я думав, що ви можете один раз визначити перехід для шару, а потім внести подальші зміни та отримати «вільний» відтінок. Здається, що вам потрібно вказати зменшення швидкості при кожній зміні. Отже - якщо ви викликаєте перехід кожен раз, це добре працює в моєму тесті на iOS9.
Плутати Ворлон

Видаліть оманливе, але, здається, він не працює в iOS9, якщо ви вважаєте, що це більше не підходить. Дякую.
SwiftArchitect

134

Швидкий 4

Правильним способом зникнення UILabel (або будь-якого UIView для цього питання) є використання a Core Animation Transition . Це не мерехтить, а також не пожовкне до чорного кольору, якщо вміст не зміниться.

Портативне та чисте рішення - використовувати Extensionв Swift (викликати попередні зміни видимих ​​елементів)

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

Виклик виглядає приблизно так:

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

Пустий, то від 1 до 5 згасання перехід


► Знайдіть це рішення на GitHub та додаткові відомості про рецепти Swift .


1
привіт. Я використовував ваш код у своєму проекті, думав, що вам може бути цікаво: github.com/goktugyil/CozyLoadingActivity
Esqarrouth

Приємно - Якщо ви можете використовувати ліцензію MIT (версію тієї самої ліцензії на адвокатські розмови), її можна було б використовувати в комерційних додатках ...
SwiftArchitect

Я не дуже розумію інформацію про ліцензії. Що заважає людям зараз використовувати його в комерційних додатках?
Esqarrouth

2
Багато компаній не можуть використовувати стандартні ліцензії, якщо їх не вказано на opensource.org/licenses, а деякі включають лише Cocoapods, що дотримуються opensource.org/licenses/MIT . Ця MITліцензія гарантує, що ваш Cocoapod може вільно користуватися всіма та будь-хто.
SwiftArchitect

2
Проблема з поточною ліцензією WTF полягає в тому, що вона не покриває ваші підстави: для початку вона не претендує на вас як на автора (авторські права) і, як такий, доводить, що ви не можете надавати пільги. У ліцензії MIT ви спочатку заявляєте право власності, яке потім використовуєте для відмови від прав. Ви дійсно повинні прочитати на MIT, якщо ви хочете, щоб професіонали використовували ваш код та брали участь у ваших зусиллях із відкритим кодом.
SwiftArchitect

20

оскільки iOS4, очевидно, це можна зробити з блоками:

[UIView animateWithDuration:1.0
                 animations:^{
                     label.alpha = 0.0f;
                     label.text = newText;
                     label.alpha = 1.0f;
                 }];

11
Не перехресний перехід.
SwiftArchitect

17

Ось код, щоб зробити цю роботу.

[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];

3
Це не згасання. Це згасання, зникнення цілком, потім згасання. Це, в основному, мерехтіння повільного руху, але це все-таки мерехтіння.
SwiftArchitect

18
будь ласка, не називайте своїх UILabels lbl
rigdonmr

5

Рішення розширення UILabel

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

}

Просто викликається функція

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

Дякую за всі поради хлопці. Сподіваюся, це допоможе в довгостроковій перспективі


4

Версія Swift 4.2 версії рішення SwiftArchitect вище (відмінно працює):

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

Виклик:

    // This will fade

aLabel.fadeTransition(0.4)
aLabel.text = "text"

3

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

АБО

UIView.animateWithDuration(0.2, animations: {
    self.sampleLabel.alpha = 1
}, completion: {
    (value: Bool) in
    self.sampleLabel.alpha = 0.2
})

1
Перший працює, але другий відразу викликає завершення роботи, незалежно від тривалості, яку я проходжу. Інша проблема полягає в тому, що я не можу натиснути жодну кнопку під час анімації першого рішення.
endavid

Щоб дозволити взаємодії під час анімації, я додав один варіант, параметри: [.TransitionCrossDissolve, .AllowUserInteraction]
endavid

У першому варіанті використання self.view змінює весь погляд, це означає, що можна використовувати лише TransitionCrossDissolve. Натомість краще перейти лише текст: "UIView.transitionWithView (self.sampleLabel, тривалість: 1.0 ...". Також у моєму випадку він краще працював із "..., завершення: нуль)".
Віталій

3

За допомогою Swift 5 ви можете вибрати один із двох наступних зразків коду Playground, щоб анімувати UILabelзміни тексту вашого тексту за допомогою деякої перехресної анімації.


№1. Використання UIView«S transition(with:duration:options:animations:completion:)метод класу

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. Використання CATransitionі CALayer's add(_:forKey:)методу

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

Обидві анімації працюють лише як crossDissolve. Все одно дякую за таку описову відповідь.
Яш Беді

2

Рішення Swift 4.2 (отримання 4.0 відповідей та оновлення для нових перерахунків для компіляції)

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

2

Системні значення за замовчуванням 0,25 для durationта .curveEaseInEaseOut для timingFunctionчасто є кращими для послідовності в анімації, і їх можна опустити:

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

що це те саме, що писати це:

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

1

Це метод розширення C # UIView, заснований на коді @ SwiftArchitect. Якщо включається автоматична компонування і елементи керування повинні переміщуватися залежно від тексту мітки, цей код виклику використовує Суперпрогляд мітки як перегляд переходу замість самого мітки. Я додав лямбда-вираз для дії, щоб зробити його більш інкапсульованим.

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

Код виклику:

  labelSuperview.FadeTransition( 0.5d, () =>
  {
    if ( condition )
      label.Text = "Value 1";
    else
      label.Text = "Value 2";
  } );

0

Якщо ви хочете це зробити Swiftіз запізненням, спробуйте:

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

                    })
                }

        })
    }

використовуючи таку функцію, яку створив @matt - https://stackoverflow.com/a/24318861/1982051 :

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

що стане цим у Swift 3

func delay(_ delay:Double, closure:()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.after(when: when, execute: closure)
}

0

Є ще одне рішення для досягнення цього. Це було описано тут . Ідея полягає в підкласифікації UILabelта переосмисленні action(for:forKey:)функції наступним чином:

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

Приклади використання:

// 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!"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.