UILabel'de metin değişikliğini canlandırın


133

A olarak yeni bir metin değeri ayarlıyorum UILabel. Şu anda, yeni metin gayet iyi görünüyor. Ancak, yeni metin göründüğünde biraz animasyon eklemek istiyorum. Yeni metnin görünümünü canlandırmak için ne yapabileceğimi merak ediyorum.


Swift 5 için bkz cevabım gösterileri 2 farklı yolu sorunu çözmek için söyledi.
Imanou Petit

Yanıtlar:


177

İşe yarayıp yaramadığını merak ediyorum ve mükemmel çalışıyor!

Objective-C

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

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

  } completion:nil];

Hızlı 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'un bu çalışmanın anahtarı olduğunu unutmayın.
akaru

7
UIView animasyon bloklarında [zayıf benlik] 'e ihtiyacınız yoktur. Bkz. Stackoverflow.com/a/27019834/511299
Sunkas

162

Objective-C

Bir başarmak için gerçek çapraz çözülür geçişi (Eski etiket kararma sırasında yeni etiket içinde solma), sen solma görünmez istemiyoruz. Metin değiştirilmese bile istenmeyen titreşime neden olur .

Bunun yerine şu yaklaşımı kullanın:

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"

Ayrıca bkz: UILabel metnini iki sayı arasında canlandırmak mı?

İOS 10, 9, 8'de gösteri:

Boş, ardından 1'den 5'e geçiş geçişi


Xcode 8.2.1 ve 7.1 , ObjectiveC ile iOS 10'dan 8.0'a kadar test edilmiştir .

► aramak, tam projeyi indirmek için SO-3073520 içinde Swift Tarifler .


Aynı anda iki etiket ayarladığımda simülatörde titreme oluyor.
huggie

1
Muhtemelen yanlış mı kullanılmış? Yaklaşımımın amacı 2 etiket kullanmamak. Bu etiket için tek bir etiket kullanırsınız -addAnimation:forKey, ardından etiketin metnini değiştirirsiniz.
SwiftArchitect

@ConfusedVorlon, lütfen ifadenizi doğrulayın. ve swiftarchitect.com/recipes/#SO-3073520 adresinde yayınlanan projeyle karşılaştır .
SwiftArchitect

Geçmişte bunun yanlış ucunu almış olabilirim. Katman için geçişi bir kez tanımlayabileceğinizi, ardından sonraki değişiklikleri yapıp 'serbest' bir solmaya sahip olabileceğinizi düşündüm. Görünüşe göre her değişimde solmayı belirtmeniz gerekiyor. Yani - her seferinde geçişi aramanız şartıyla, bu iOS9'daki testimde iyi çalışıyor.
Confused Vorlon

Lütfen yanıltıcı olanı kaldırın, ancak artık uygun olmadığını düşünüyorsanız iOS9'da çalışmıyor gibi görünüyor . Teşekkür ederim.
SwiftArchitect

134

Swift 4

Bir UILabel'i (veya bu konuda herhangi bir UIView) soldurmanın uygun yolu, bir Core Animation Transition. Bu titremeyecek ve içerik değişmemişse siyaha dönmeyecektir.

Taşınabilir ve temiz bir çözüm, ExtensionSwift'de a kullanmaktır (görünür öğeleri değiştirmeden önce çağırın)

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

Çağrı şuna benzer:

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

Boş, ardından 1'den 5'e geçiş geçişi


GitHub'da bu çözümü ve Swift Tarifleriyle ilgili ek ayrıntıları bulun .


1
merhaba, kodunuzu projemde
Esqarrouth

Güzel - MIT lisansını (aynı lisansın avukat konuşma sürümü) kullanabilseydiniz, ticari uygulamalarda kullanılabilir ...
SwiftArchitect

Ehliyet meselesini gerçekten anlamıyorum. İnsanların artık ticari uygulamalarda kullanmasını engelleyen nedir?
Esqarrouth

2
Birçok şirket, opensource.org/licenses'de listelenmedikçe standart lisansları kullanamaz ve bazıları yalnızca opensource.org/licenses/MIT'e bağlı kalarak Cocoapod'ları dahil eder . Bu MITlisans, Cocoapod'unuzun herkes ve herkes tarafından özgürce kullanılabileceğini garanti eder.
SwiftArchitect

2
Mevcut WTF Lisansıyla ilgili sorun, gerekçenizi kapsamamasıdır: yeni başlayanlar için, sizi yazar (telif hakkı) olarak iddia etmez ve bu nedenle, ayrıcalıklar verebileceğinizi kanıtlamaz. MIT Lisansında, önce sahiplik talebinde bulunursunuz, daha sonra bu haklardan vazgeçmek için kullanırsınız. Profesyonellerin kodunuzdan yararlanmasını ve açık kaynak çabalarınıza katılmasını istiyorsanız, gerçekten MIT hakkında okumalısınız.
SwiftArchitect

20

iOS4'ten beri açıkça bloklarla yapılabilir:

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

11
Çapraz solmaya geçiş değil.
SwiftArchitect

17

İşte bunun çalışmasını sağlayacak kod.

[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
Bu bir solma değil. Bu bir kararma, tamamen kaybolma, sonra belirme. Temelde yavaş hareket eden bir titreme, ama yine de bir titreme.
SwiftArchitect

18
lütfen UILabels lbl
rigdonmr 10'17

5

UILabel Uzatma Çözümü

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 işlevi tarafından

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

Tüm ipuçları için teşekkürler çocuklar. Umarım bu uzun vadede yardımcı olur


4

SwiftArchitect'in yukarıdaki çözümünün Swift 4.2 sürümü (harika çalışıyor):

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

çağırma:

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

VEYA

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

1
İlki çalışıyor, ancak ikincisi, geçtiğim süre ne olursa olsun hemen tamamlamayı çağırıyor. Diğer bir sorun da ilk çözümle canlandırırken herhangi bir düğmeye basamıyorum.
endavid

Animasyon sırasında etkileşimlere izin vermek için bir seçenek ekledim, seçenekler: [.TransitionCrossDissolve, .AllowUserInteraction]
endavid

İlk seçenekte self.view kullanmak tüm görünümü değiştirir, bu sadece TransitionCrossDissolve kullanılabileceği anlamına gelir. Bunun yerine, yalnızca şu metni değiştirmek daha iyidir: "UIView.transitionWithView (self.sampleLabel, süre: 1.0 ...". Ayrıca benim durumumda "..., complete: nil)" ile daha iyi çalıştı.
Vitalii

3

Swift 5 ile, UILabelbazı çapraz çözülme animasyonlarıyla metin değişikliklerinizi canlandırmak için aşağıdaki iki Playground kod örneğinden birini seçebilirsiniz .


# 1. Kullanma UIViews' transition(with:duration:options:animations:completion:)sınıf yöntemi

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. Kullanma CATransitionve CALayerbireyin add(_:forKey:)yöntemi

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

Her iki animasyon da yalnızca crossDissolve gibi çalışır. Her neyse, böyle açıklayıcı bir cevap için teşekkür ederim.
Yash Bedi

2

Swift 4.2 çözümü (4.0 yanıtını alma ve yeni numaralandırmaların derlenmesi için güncelleme)

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 için durationve .curveEaseInEaseOut için sistem varsayılan değerleri, timingFunctionanimasyonlarda tutarlılık için genellikle tercih edilir ve atlanabilir:

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

bu, bunu yazmakla aynı:

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

1

Bu, @ SwiftArchitect'in kodunu temel alan bir C # UIView genişletme yöntemidir. Otomatik düzen söz konusu olduğunda ve kontrollerin etiketin metnine bağlı olarak hareket etmesi gerektiğinde, bu çağrı kodu, etiketin kendisi yerine geçiş görünümü olarak etiketin Denetleme görünümünü kullanır. Eylemi daha kapsüllenmiş hale getirmek için bir lambda ifadesi ekledim.

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

Arama kodu:

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

0

Bunu Swiftgecikmeli olarak yapmak istiyorsanız, şunu deneyin:

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 tarafından oluşturulan şu işlevi kullanarak - 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'te bu olacak

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

0

Bunu başarmak için bir çözüm daha var. Bu nitelendirildi burada . Fikir, alt sınıflandırma UILabelve action(for:forKey:)işlevi aşağıdaki şekilde geçersiz kılma :

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

Kullanım örnekleri:

// 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!"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.