Animasi teks berubah dalam UILabel

133

Saya menetapkan nilai teks baru ke a UILabel. Saat ini, teks baru muncul dengan baik. Namun, saya ingin menambahkan beberapa animasi saat teks baru muncul. Saya bertanya-tanya apa yang bisa saya lakukan untuk menghidupkan tampilan teks baru.

Taman Joo
sumber
Untuk Swift 5, lihat jawaban saya yang menunjukkan 2 cara berbeda untuk menyelesaikan masalah Anda.
Imanou Petit

Jawaban:

177

Saya ingin tahu apakah itu berfungsi, dan itu bekerja dengan sempurna!

Objektif-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)
Anton Gaenko
sumber
16
Perhatikan bahwa TransitionCrossDissolve adalah kunci untuk kerja ini.
akaru
7
Anda tidak perlu [diri lemah] di blok animasi UIView. Lihat stackoverflow.com/a/27019834/511299
Sunkas
162

Objektif-C

Untuk mencapai benar cross-larut transisi (label lama memudar sementara label baru memudar dalam), Anda tidak ingin memudar untuk tak terlihat. Ini akan menghasilkan flicker yang tidak diinginkan bahkan jika teks tidak berubah .

Gunakan pendekatan ini sebagai gantinya:

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"

Lihat juga: Menghidupkan teks UILabel di antara dua angka?

Demonstrasi di iOS 10, 9, 8:

Kosong, lalu transisi 1 hingga 5 pudar


Diuji dengan Xcode 8.2.1 & 7.1 , ObjectiveC di iOS 10 hingga 8.0 .

► Untuk mengunduh proyek lengkap, cari SO-3073520 di Swift Recipes .

Arsitektur Swift
sumber
Ketika saya mengatur dua label pada saat yang sama memberikan flicker pada simulator.
huggie
1
Mungkin disalahgunakan? Inti dari pendekatan saya adalah tidak menggunakan 2 label. Anda menggunakan label tunggal, -addAnimation:forKeyuntuk label itu, lalu ubah teks labelnya.
SwiftArchitect
@ConfusedVorlon, harap verifikasi pernyataan Anda. dan periksa proyek yang diposting di swiftarchitect.com/recipes/#SO-3073520 .
SwiftArchitect
Saya mungkin salah paham tentang ini di masa lalu. Saya pikir Anda bisa mendefinisikan transisi sekali untuk layer, lalu membuat perubahan berikutnya dan mendapatkan pudar 'gratis'. Tampaknya Anda harus menentukan fade pada setiap perubahan. Jadi - asalkan Anda memanggil transisi setiap kali, ini berfungsi dengan baik dalam pengujian saya di iOS9.
Vorlon yang Bingung
Harap hapus yang menyesatkan tetapi tampaknya tidak berfungsi di iOS9 jika Anda merasa itu tidak sesuai lagi. Terima kasih.
SwiftArchitect
134

Cepat 4

Cara yang tepat untuk memudar label UIL (atau UIView dalam hal ini) adalah dengan menggunakan a Core Animation Transition. Ini tidak akan berkedip, juga tidak akan pudar menjadi hitam jika kontennya tidak berubah.

Solusi portabel dan bersih adalah dengan menggunakan Extensionin Swift (aktifkan elemen yang terlihat sebelum berubah)

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

Doa terlihat seperti ini:

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

Kosong, lalu transisi 1 hingga 5 pudar


► Temukan solusi ini di GitHub dan detail tambahan tentang Resep Swift .

Arsitektur Swift
sumber
1
halo saya menggunakan kode Anda dalam proyek saya, saya pikir Anda mungkin tertarik: github.com/goktugyil/CozyLoadingActivity
Esqarrouth
Bagus - Jika Anda dapat menggunakan lisensi MIT (versi pengacara-bicara dari lisensi yang sama), itu dapat digunakan dalam aplikasi komersial ...
SwiftArchitect
Saya tidak begitu mengerti soal lisensi. Apa yang mencegah orang menggunakannya di aplikasi komersial sekarang?
Esqarrouth
2
Banyak perusahaan tidak dapat menggunakan lisensi standar kecuali terdaftar di opensource.org/licenses dan beberapa hanya akan memasukkan Cocoapods yang mengikuti opensource.org/licenses/MIT . Itu MITlisensi menjamin Cocoapod Anda dapat digunakan secara bebas oleh semua orang dan siapa saja.
SwiftArchitect
2
Masalah dengan Lisensi WTF saat ini adalah bahwa ia tidak mencakup alasan Anda: sebagai permulaan, ia tidak mengklaim Anda sebagai penulis (hak cipta), dan dengan demikian, membuktikan bahwa Anda tidak dapat memberikan hak istimewa. Dalam Lisensi MIT, Anda pertama-tama mengklaim kepemilikan, yang kemudian Anda gunakan untuk melepaskan hak. Anda harus benar-benar membaca di MIT jika Anda ingin para profesional memanfaatkan kode Anda, dan berpartisipasi dalam upaya open source Anda.
SwiftArchitect
20

karena iOS4 dapat dengan jelas dilakukan dengan blok:

[UIView animateWithDuration:1.0
                 animations:^{
                     label.alpha = 0.0f;
                     label.text = newText;
                     label.alpha = 1.0f;
                 }];
Mapedd
sumber
11
Bukan transisi lintas fade.
SwiftArchitect
17

Berikut adalah kode untuk membuatnya berfungsi.

[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];
Taman Joo
sumber
3
Ini bukan memudar. Ini adalah fade out, menghilang seluruhnya, lalu fade in. Ini pada dasarnya adalah gerakan lambat yang berkedip, tetapi masih berkedip.
SwiftArchitect
18
tolong jangan sebutkan label UIL Anda lbl
rigdonmr
5

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

}

Cukup dipanggil fungsi oleh

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

Terima kasih untuk semua tipsnya kawan. Semoga ini bisa membantu dalam jangka panjang

Muhammad Asyraf
sumber
4

Swift versi 4.2 dari solusi SwiftArchitect di atas (berfungsi dengan baik):

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

Doa:

    // This will fade

aLabel.fadeTransition(0.4)
aLabel.text = "text"
winnie-ru
sumber
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"
})

ATAU

UIView.animateWithDuration(0.2, animations: {
    self.sampleLabel.alpha = 1
}, completion: {
    (value: Bool) in
    self.sampleLabel.alpha = 0.2
})
AG
sumber
1
Yang pertama berhasil, tetapi yang kedua hanya memanggil penyelesaian segera, terlepas dari durasi yang saya lewati. Masalah lainnya adalah saya tidak dapat menekan tombol apa pun saat saya menghidupkan solusi pertama.
endavid
Untuk memungkinkan interaksi sambil beranimasi, saya menambahkan satu opsi, opsi: [.TransitionCrossDissolve, .AllowUserInteraction]
endavid
Pada opsi pertama menggunakan self.view membuat seluruh tampilan berubah, ini berarti hanya TransitionCrossDissolve yang dapat digunakan. Alih-alih lebih baik hanya transisi teks: "UIView.transitionWithView (self.sampleLabel, durasi: 1.0 ...". Juga dalam kasus saya ini berfungsi lebih baik dengan "..., completion: nil)".
Vitalii
3

Dengan Swift 5, Anda dapat memilih salah satu dari dua contoh kode Playground berikut untuk menganimasikan UILabelperubahan teks Anda dengan beberapa animasi lintas silang.


# 1. Menggunakan UIView's transition(with:duration:options:animations:completion:)metode kelas

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. Menggunakan CATransitiondan CALayer's add(_:forKey:)metode

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
Imanou Petit
sumber
Kedua animasi berfungsi seperti crossDissolve saja. Bagaimanapun terima kasih atas jawaban yang deskriptif.
Yash Bedi
2

Solusi Swift 4.2 (mengambil jawaban 4.0 dan memperbarui kompilasi enum baru)

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"
}
Chris
sumber
2

Nilai default sistem 0,25 untuk durationdan .curveEaseInEaseOut untuk timingFunctionsering lebih disukai untuk konsistensi di seluruh animasi, dan dapat dihilangkan:

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

yang sama dengan menulis ini:

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

Ini adalah metode ekstensi C # UIView yang didasarkan pada kode @ SwiftArchitect. Ketika tata letak otomatis terlibat dan kontrol perlu bergerak tergantung pada teks label, kode panggilan ini menggunakan Superview label sebagai tampilan transisi, bukan label itu sendiri. Saya menambahkan ekspresi lambda untuk tindakan agar lebih dienkapsulasi.

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

Kode panggilan:

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

Jika Anda ingin melakukan ini Swiftdengan penundaan, coba ini:

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

                    })
                }

        })
    }

menggunakan fungsi berikut yang dibuat oleh @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)
}

yang akan menjadi ini di Swift 3

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

Ada satu solusi lagi untuk mencapai ini. Itu dijelaskan di sini . Idenya adalah fungsi subclassing UILabeldan override action(for:forKey:)dengan cara berikut:

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

Contoh penggunaan:

// 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!"
Roman Aliyev
sumber