การเปลี่ยนแปลงข้อความเคลื่อนไหวใน UILabel


133

ฉันกำลังตั้งค่าข้อความใหม่เป็นUILabelไฟล์. ขณะนี้ข้อความใหม่ปรากฏขึ้นตามปกติ อย่างไรก็ตามฉันต้องการเพิ่มภาพเคลื่อนไหวเมื่อข้อความใหม่ปรากฏขึ้น ฉันสงสัยว่าฉันจะทำอะไรได้บ้างเพื่อให้ข้อความใหม่เคลื่อนไหว


สำหรับ Swift 5 โปรดดูคำตอบของฉันที่แสดง 2 วิธีต่างๆในการแก้ปัญหาของคุณ
Imanou Petit

คำตอบ:


177

ฉันสงสัยว่ามันใช้งานได้หรือไม่และทำงานได้อย่างสมบูรณ์แบบ!

Objective-C

[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

Objective-C

เพื่อให้เกิดการเปลี่ยนแปลงข้ามการละลายอย่างแท้จริง (ฉลากเก่าจะจางหายไปในขณะที่ฉลากใหม่จางลง) คุณไม่ต้องการให้เลือนหายไปจนมองไม่เห็น มันจะส่งผลให้เกิดการสั่นไหวที่ไม่ต้องการแม้ว่าข้อความจะไม่เปลี่ยนแปลงก็ตามก็ตาม

ใช้แนวทางนี้แทน:

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ในสวิฟท์สูตร


เมื่อฉันตั้งป้ายกำกับสองป้ายพร้อมกันมันจะทำให้เกิดการสั่นไหวบนตัวจำลอง
huggie

1
อาจใช้ในทางที่ผิด? แนวทางของผมคืออย่าใช้ป้าย 2 ป้าย คุณใช้ป้ายกำกับเดียว-addAnimation:forKeyกับป้ายนั้นจากนั้นเปลี่ยนข้อความของป้ายกำกับ
SwiftArchitect

@ConfusedVorlon โปรดตรวจสอบใบแจ้งยอดของคุณ และตรวจสอบกับโครงการโพสต์ที่swiftarchitect.com/recipes/#SO-3073520
SwiftArchitect

ฉันอาจจะมีจุดจบที่ไม่ถูกต้องในเรื่องนี้ในอดีต ฉันคิดว่าคุณสามารถกำหนดการเปลี่ยนแปลงหนึ่งครั้งสำหรับเลเยอร์จากนั้นทำการเปลี่ยนแปลงในภายหลังและรับการจางแบบ 'ฟรี' ดูเหมือนว่าคุณต้องระบุการจางในการเปลี่ยนแปลงแต่ละครั้ง ดังนั้น - หากคุณโทรหาการเปลี่ยนแปลงทุกครั้งสิ่งนี้ใช้ได้ดีในการทดสอบของฉันบน iOS9
สับสน Vorlon

โปรดลบสิ่งที่ทำให้เข้าใจผิดแต่ดูเหมือนจะใช้ไม่ได้ใน iOS9หากคุณคิดว่ามันไม่เหมาะสมอีกต่อไป ขอบคุณ.
SwiftArchitect

134

สวิฟต์ 4

วิธีที่เหมาะสมในการทำให้ UILabel จางหายไป (หรือ UIView ใด ๆ สำหรับเรื่องนั้น) คือการใช้ไฟล์ Core Animation Transition . สิ่งนี้จะไม่สั่นไหวและจะไม่จางหายไปเป็นสีดำหากเนื้อหาไม่เปลี่ยนแปลง

โซลูชันแบบพกพาและสะอาดคือการใช้ExtensionSwift (เรียกใช้ก่อนที่จะเปลี่ยนองค์ประกอบที่มองเห็นได้)

// 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และรายละเอียดเพิ่มเติมเกี่ยวกับสวิฟท์สูตร


1
สวัสดีฉันใช้รหัสของคุณในโครงการของฉันคิดว่าคุณอาจสนใจ: github.com/goktugyil/CozyLoadingActivity
Esqarrouth

Nice - ถ้าคุณสามารถใช้ใบอนุญาต 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

โซลูชันของ SwiftArchitect เวอร์ชัน Swift 4.2 ด้านบน (ใช้งานได้ดี):

    // 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 ... " นอกจากนี้ในกรณีของฉันมันทำงานได้ดีกว่ากับ "... , complete: nil)"
Vitalii

3

ด้วย Swift 5 คุณสามารถเลือกหนึ่งในสองตัวอย่างโค้ด Playground ต่อไปนี้เพื่อทำให้UILabelการเปลี่ยนแปลงข้อความของคุณเคลื่อนไหวด้วยภาพเคลื่อนไหวที่ละลายได้


# 1 ใช้UIViewเป็น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ของ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 เท่านั้น ยังไงก็ตามขอบคุณสำหรับคำตอบที่สื่อความหมาย
Yash Bedi

2

โซลูชัน Swift 4.2 (รับ 4.0 คำตอบและอัปเดตสำหรับ enums ใหม่เพื่อรวบรวม)

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 เมื่อมีส่วนเกี่ยวข้องกับการจัดวางอัตโนมัติและการควบคุมจำเป็นต้องย้ายโดยขึ้นอยู่กับข้อความของป้ายกำกับรหัสการเรียกนี้จะใช้ Superview ของป้ายกำกับเป็นมุมมองการเปลี่ยนแทนป้ายกำกับเอง ฉันเพิ่มนิพจน์แลมบ์ดาสำหรับการกระทำเพื่อให้มีการห่อหุ้มมากขึ้น

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!"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.