How to Animate Uilabel's Textcolor Change

How to animate the textColor property of an UILabel?

This answer is obsolete, and is not a good solution for the original question. @strange 's answer below is much better and should be used instead of this answer: https://stackoverflow.com/a/20892927/76559

//Old answer below

The textColor property is not specified as being animatable in the docs, so I don't think you can do it with a simple UIView animations block...

This could probably be done pretty crudely with an NSTimer firing every couple of milliseconds, each time setting the colour gradually from one to the other.

I say this is crude because it would require an array or some other container of preset colour values going from the start colour to the finish colour, and I'm sure there's a way you could do this using core animation or something, I just don't know what it is.

Is it possible to animate UILabel's textcolor change?

Try this

[UIView transitionWithView:myLabel duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
label.textColor = [UIColor redColor];
} completion:^(BOOL finished) {
}];

Animate UILabel text color in Swift

it is much easier than working with CALayer

let myLabel: UILabel!

UIView.animateWithDuration(2, animations: { () -> Void in
myLabel.backgroundColor = UIColor.redColor();
})

Thats it...

Edit

Ok, sorry I didn't knew what color you want to change... I have converted your example to swift code...

first

import QuartzCore

than

if let layer: CALayer = self.view.layer as CALayer? {
if let textLayer = CATextLayer() as CATextLayer? {
textLayer.string = "My string"
textLayer.foregroundColor = UIColor.whiteColor().CGColor
textLayer.frame = self.view.bounds
self.view.layer.addSublayer(textLayer)

UIView.animateWithDuration(0.5, animations: { () -> Void in
textLayer.foregroundColor = UIColor.redColor().CGColor
})
}
}

Is is possible to animate text color changing only in a part of text in iOS?

You can quite easily achieve this using CoreAnimation possibilities.
I've added a simple demo, you play with it here (just build the project and tap anywhere to see the animation).

The logic is the following:

  1. Create a custom subclass of UIView.
  2. When some text is set, create two similar CATextLayers, each with the same text and frame.
  3. Set different foregroundColor and mask for those layers. The mask of the left layer will be the left part of the view, and the mask of the right layer will be the right part.
  4. Animate foregroundColor for those layers (simultaneously).

The code of a custom view:

class CustomTextLabel: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .green
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private var textLayer1: CATextLayer?
private var textLayer2: CATextLayer?

func setText(_ text: String, fontSize: CGFloat) {
// create 2 layers with the same text and size, we'll set the colors for them later
textLayer1 = createTextLayer(text, fontSize: fontSize)
textLayer2 = createTextLayer(text, fontSize: fontSize)

// estimate the frame size needed for the text layer with such text and font size
let textSize = textLayer1!.preferredFrameSize()
let w = frame.width, h = frame.height

// calculate the frame such that both layers will be in center of view
let centeredTextFrame = CGRect(x: (w-textSize.width)/2, y: (h-textSize.height)/2, width: textSize.width, height: textSize.height)
textLayer1!.frame = centeredTextFrame
textLayer2!.frame = centeredTextFrame

// set up default color for the text
textLayer1!.foregroundColor = UIColor.yellow.cgColor
textLayer2!.foregroundColor = UIColor.yellow.cgColor

// set background transparent, that's very important
textLayer1!.backgroundColor = UIColor.clear.cgColor
textLayer2!.backgroundColor = UIColor.clear.cgColor

// set up masks, such that each layer's text is visible only in its part
textLayer1!.mask = createMaskLayer(CGRect(x: 0, y: 0, width: textSize.width/2, height: textSize.height))
textLayer2!.mask = createMaskLayer(CGRect(x: textSize.width/2, y: 0, width: textSize.width/2, height: textSize.height))

layer.addSublayer(textLayer1!)
layer.addSublayer(textLayer2!)
}

private var finishColor1: UIColor = .black, finishColor2: UIColor = .black
func animateText(leftPartColor1: UIColor, leftPartColor2: UIColor, rightPartColor1: UIColor, rightPartColor2: UIColor) {
finishColor1 = leftPartColor2
finishColor2 = rightPartColor2

if let layer1 = textLayer1, let layer2 = textLayer2 {
CATransaction.begin()
let animation1 = CABasicAnimation(keyPath: "foregroundColor")
animation1.fromValue = leftPartColor1.cgColor
animation1.toValue = leftPartColor2.cgColor
animation1.duration = 3.0
layer1.add(animation1, forKey: "animation1")

let animation2 = CABasicAnimation(keyPath: "foregroundColor")
animation2.fromValue = rightPartColor1.cgColor
animation2.toValue = rightPartColor2.cgColor
animation2.duration = 3.0
layer2.add(animation2, forKey: "animation2")

CATransaction.setCompletionBlock {
self.textLayer1?.foregroundColor = self.finishColor1.cgColor
self.textLayer2?.foregroundColor = self.finishColor2.cgColor
}

CATransaction.commit()
}
}

private func createTextLayer(_ text: String, fontSize: CGFloat) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.string = text
textLayer.fontSize = fontSize // TODO: also set font name
textLayer.contentsScale = UIScreen.main.scale

return textLayer
}

private func createMaskLayer(_ holeRect: CGRect) -> CAShapeLayer {
let layer = CAShapeLayer()

let path = CGMutablePath()

path.addRect(holeRect)
path.addRect(bounds)

layer.path = path
layer.fillRule = CAShapeLayerFillRule.evenOdd
layer.opacity = 1

return layer
}
}

The calls of a custom view:

class ViewController: UIViewController {

var customLabel: CustomTextLabel!
override func viewDidLoad() {
super.viewDidLoad()

let viewW = view.frame.width, viewH = view.frame.height
let labelW: CGFloat = 200, labelH: CGFloat = 50

customLabel = CustomTextLabel(frame: CGRect(x: (viewW-labelW)/2, y: (viewH-labelH)/2, width: labelW, height: labelH))
customLabel.setText("Optimizing...", fontSize: 20)
view.addSubview(customLabel)

let tapRecogniner = UITapGestureRecognizer(target: self, action: #selector(onTap))
view.addGestureRecognizer(tapRecogniner)
}

@objc func onTap() {
customLabel.animateText(leftPartColor1: UIColor.blue,
leftPartColor2: UIColor.red,
rightPartColor1: UIColor.white,
rightPartColor2: UIColor.black)
}

}

How to animate text color of a UILabel like a progress bar

To achieve it, follow below steps:

  • Create 2 UILabel (trackLabel and progressLabel).
  • trackLabel have blackColor and progressLabel have redColor.
  • Make progressLabel overlap trackLabel, same leading, top and bottom.
  • Give trackLabel full width with text and progressLabel 0 width.
  • Set progressLabel.lineBreakMode = NSLineBreakByClipping;
  • When you need to animate progress, increase width of progressLabel.

For more detail, you can check my demo project.

enter link description here

How to animate (fade) UILabel textColor in Swift?

UILabel.textColor does not support animation. But you can animate CATextLayer:

Swift:

let textLayer = CATextLayer()
textLayer.string = "Your text"
textLayer.foregroundColor = yourFirstColor
textLayer.frame = yourButton.bounds
yourButton.layer.addSublayer(textLayer)

UIView.animateWithDuration(1) {
textLayer.foregroundColor = yourSecondColor
}

Objective C:

CATextLayer *textLayer = [CATextLayer layer];
[textLayer setString:@"Your text"];
[textLayer setForegroundColor:yourFirstColor];
[textLayer setFrame:yourButton.bounds];
[[yourButton layer] addSublayer:textLayer];

[UIView animateWithDuration:1 animations:^{
textLayer.foregroundColor = yourSecondColor;
}];

Animating UILabel textColor property with UIViewPropertyAnimator

As said in the comments, the textColor property can not be animated.
However, there is a technique called color interpolation, which might be a nice workaround.

You can find multiple ways to solve this in this thread, however, I provide you with one solution also:

extension UIColor {
var components: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
let components = self.cgColor.components!

switch components.count == 2 {
case true :
return (r: components[0], g: components[0], b: components[0], a: components[1])
case false:
return (r: components[0], g: components[1], b: components[2], a: components[3])
}
}

static func interpolate(from fromColor: UIColor, to toColor: UIColor, with progress: CGFloat) -> UIColor {
let fromComponents = fromColor.components
let toComponents = toColor.components

let r = (1 - progress) * fromComponents.r + progress * toComponents.r
let g = (1 - progress) * fromComponents.g + progress * toComponents.g
let b = (1 - progress) * fromComponents.b + progress * toComponents.b
let a = (1 - progress) * fromComponents.a + progress * toComponents.a

return UIColor(red: r, green: g, blue: b, alpha: a)
}
}

Then, all you have to do is this in the function you call when the slider's value has changed:

@objc func sliderChanged(_ sender: UISlider) {
animator.fractionComplete = CGFloat(sender.value)
yourLabel.textColor = UIColor.interpolate(from: fromColor, to: toColor, with: sender.value)
}

You are not animating the textColor, rather you change it to a different color with each call to sliderChanged, but the change appears gradual and does not jump from your start color to your second color, so I think it should achieve your desired result.

How to animate text color change partially in UILabel with custom control?

You could do this by having your white selector act as a mask. Duplicate your three labels -- one set for white, the other for grey -- and position them overlapping on each other. Add the three grey ones as subviews of the white selector and cause the white selector to clipsToBounds. When you animate the selector, animate the subview labels in the opposite direction at the same rate and they will appear to stay in place but become revealed as your white bar moves.



Related Topics



Leave a reply



Submit