Thay đổi văn bản hoạt hình trong UILabel


133

Tôi đang đặt một giá trị văn bản mới cho a UILabel. Hiện tại, văn bản mới xuất hiện tốt. Tuy nhiên, tôi muốn thêm một số hình ảnh động khi văn bản mới xuất hiện. Tôi đang tự hỏi tôi có thể làm gì để làm sinh động sự xuất hiện của văn bản mới.


Đối với Swift 5, hãy xem câu trả lời của tôi chỉ ra 2 cách khác nhau để giải quyết vấn đề của bạn.
Imanou Petit

Câu trả lời:


177

Tôi tự hỏi nếu nó hoạt động, và nó hoạt động hoàn hảo!

Mục tiêu-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)

16
Lưu ý rằng TransitionCrossDissolve là chìa khóa để làm việc này.
akaru

7
Bạn không cần [bản thân yếu] trong các khối hoạt hình UIView. Xem stackoverflow.com/a/27019834/511299
Sunkas

162

Mục tiêu-C

Để đạt được chuyển đổi hòa tan chéo thực sự (nhãn cũ mờ dần trong khi nhãn mới mờ dần), bạn không muốn mờ dần thành vô hình. Nó sẽ dẫn đến nhấp nháy không mong muốn ngay cả khi văn bản không thay đổi .

Sử dụng phương pháp này thay thế:

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"

Xem thêm: Animate văn bản UILabel giữa hai số?

Trình diễn trong iOS 10, 9, 8:

Để trống, sau đó chuyển từ 1 đến 5


Đã thử nghiệm với Xcode 8.2.1 & 7.1 , ObjectiveC trên iOS 10 đến 8.0 .

► Để tải xuống toàn bộ dự án, tìm kiếm SO-3073520 trong Swift Recipes .


Khi tôi đặt hai nhãn cùng một lúc, nó sẽ nhấp nháy trên trình giả lập.
huggie

1
Có thể sử dụng sai? Quan điểm của tôi là không sử dụng 2 nhãn. Bạn sử dụng một nhãn duy nhất, -addAnimation:forKeycho nhãn đó, sau đó thay đổi văn bản của nhãn.
SwiftArchitect

@ConfuseVorlon, vui lòng xác minh tuyên bố của bạn. và kiểm tra đối với dự án được đăng tại swiftarchitect.com/recipes/#SO-3073520 .
SwiftArchitect

Tôi có thể đã có kết thúc sai lầm của thanh này trong quá khứ. Tôi nghĩ rằng bạn có thể xác định quá trình chuyển đổi một lần cho lớp, sau đó thực hiện các thay đổi tiếp theo và nhận được độ mờ 'miễn phí'. Có vẻ như bạn phải chỉ định độ mờ dần ở mỗi thay đổi. Vì vậy - miễn là bạn gọi chuyển đổi mỗi lần, điều này hoạt động tốt trong thử nghiệm của tôi trên iOS9.
Vorlon bối rối

Vui lòng xóa thông tin sai lệch nhưng dường như không hoạt động trong iOS9 nếu bạn cảm thấy nó không còn phù hợp. Cảm ơn bạn.
SwiftArchitect 9/11/2015

134

Swift 4

Cách thích hợp để làm mờ UILabel (hoặc bất kỳ UIView nào cho vấn đề đó) là sử dụng a Core Animation Transition. Điều này sẽ không nhấp nháy, cũng sẽ không phai thành màu đen nếu nội dung không thay đổi.

Một giải pháp di động và sạch sẽ là sử dụng Extensiontrong Swift (gọi các phần tử hiển thị thay đổi trước đó)

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

Lời mời trông như thế này:

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

Để trống, sau đó chuyển từ 1 đến 5


► Tìm giải pháp này trên GitHub và các chi tiết bổ sung trên Swift Recipes .


1
xin chào tôi đã sử dụng mã của bạn trong dự án của tôi, nghĩ rằng bạn có thể quan tâm: github.com/goktugyil/CozyLoadingActivity
Esqarrouth

Nice - Nếu bạn có thể sử dụng giấy phép MIT (phiên bản luật sư nói về cùng loại giấy phép đó), nó có thể được sử dụng trong các ứng dụng thương mại ...
SwiftArchitect

Tôi không thực sự hiểu về những thứ giấy phép. Điều gì ngăn cản mọi người sử dụng nó trong các ứng dụng thương mại bây giờ?
Esqarrouth

2
Nhiều công ty không thể sử dụng giấy phép tiêu chuẩn trừ khi được liệt kê trên opensource.org/licenses và một số sẽ chỉ kết hợp Cocoapods tuân thủ opensource.org/licenses/MIT . MITGiấy phép đó đảm bảo Cocoapod của bạn có thể được sử dụng tự do bởi mọi người và bất cứ ai.
SwiftArchitect

2
Vấn đề với Giấy phép WTF hiện tại là nó không bao gồm các căn cứ của bạn: đối với người mới bắt đầu, nó không yêu cầu bạn là tác giả (bản quyền), và như vậy, chứng tỏ rằng bạn không thể cho phép đặc quyền. Trong Giấy phép MIT, trước tiên bạn yêu cầu quyền sở hữu, sau đó bạn sử dụng để từ bỏ quyền. Bạn thực sự nên đọc lên MIT nếu bạn muốn các chuyên gia tận dụng mã của bạn và tham gia vào nỗ lực nguồn mở của bạn.
SwiftArchitect

20

kể từ iOS4, rõ ràng nó có thể được thực hiện với các khối:

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

11
Không phải là một sự chuyển tiếp mờ dần.
SwiftArchitect

17

Đây là mã để làm cho công việc này.

[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
Đây không phải là một sự phai nhạt. Đây là một sự mờ dần, biến mất hoàn toàn, sau đó mờ dần. Về cơ bản, nó là một nhấp nháy chuyển động chậm, nhưng nó vẫn là một nhấp nháy.
SwiftArchitect

18
vui lòng không đặt tên cho UILabels lbl của bạn
Rigdonmr

5

Giải pháp mở rộng 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)
  }

}

Chức năng gọi đơn giản là

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

Cảm ơn sự hướng dẫn. Hy vọng điều này sẽ giúp lâu dài


4

Phiên bản Swift 4.2 của giải pháp SwiftArchitect ở trên (hoạt động rất tốt):

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

Cầu nguyện:

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

HOẶC LÀ

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

1
Cái đầu tiên hoạt động, nhưng cái thứ hai chỉ cần hoàn thành ngay lập tức, bất kể thời gian tôi vượt qua. Một vấn đề khác là tôi không thể nhấn bất kỳ nút nào trong khi tôi đang hoạt hình với giải pháp đầu tiên.
endavid

Để cho phép các tương tác trong khi hoạt hình, tôi đã thêm một tùy chọn, tùy chọn: [.TransitionCrossDissolve,. ALLowUserInteraction]
endavid

Trong tùy chọn đầu tiên sử dụng self.view làm cho toàn bộ khung nhìn thay đổi, điều này có nghĩa là chỉ TransitionCrossDissolve mới có thể được sử dụng. Thay vào đó, tốt hơn là chỉ chuyển đổi văn bản: "UIView.transitionWithView (self.sampleLabel, thời gian: 1.0 ...". Ngoài ra, trong trường hợp của tôi, nó hoạt động tốt hơn với "..., hoàn thành: nil)".
Vitalii

3

Với Swift 5, bạn có thể chọn một trong hai mẫu mã Playground sau để tạo hiệu ứng UILabelthay đổi văn bản của bạn với một số hình ảnh động hòa tan chéo.


# 1. Sử dụng UIViewtransition(with:duration:options:animations:completion:)phương pháp lớp

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. Sử dụng CATransitionCALayer's add(_:forKey:)phương pháp

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

Cả hai hình ảnh động chỉ hoạt động như crossDissolve. Dù sao cũng cảm ơn bạn cho một câu trả lời mô tả như vậy.
Yash Bedi

2

Giải pháp Swift 4.2 (lấy câu trả lời 4.0 và cập nhật cho các enum mới để biên dịch)

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

Các giá trị mặc định của hệ thống là 0,25 cho durationvà .curveEaseInEaseOut timingFunctionthường được ưu tiên hơn cho tính nhất quán trong các hình ảnh động và có thể được bỏ qua:

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

Điều này giống như viết này:

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

1

Đây là phương thức mở rộng C # UIView dựa trên mã của @ SwiftArchitect. Khi bố trí tự động có liên quan và các điều khiển cần di chuyển tùy thuộc vào văn bản của nhãn, mã gọi này sử dụng Superview của nhãn làm chế độ xem chuyển tiếp thay vì chính nhãn. Tôi đã thêm một biểu thức lambda cho hành động để làm cho nó được gói gọn hơn.

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

Mã gọi:

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

0

Nếu bạn muốn làm điều này Swiftvới sự chậm trễ, hãy thử điều này:

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

                    })
                }

        })
    }

sử dụng chức năng sau được tạo bởi @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)
}

sẽ trở thành thứ này trong Swift 3

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

0

Có một giải pháp nữa để đạt được điều này. Nó đã được mô tả ở đây . Ý tưởng là phân lớp UILabelaction(for:forKey:)chức năng ghi đè theo cách sau:

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

Ví dụ sử dụng:

// 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!"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.