How to animate (fade) UILabel textColor in Swift? - ios

UIView.animateWithDuration(self.fadeTime, delay: 0,
options: UIViewAnimationOptions.AllowUserInteraction,
animations: { [weak self] () -> Void in
var randomIndex = Int(arc4random_uniform(UInt32(CONSTANTS.MainColorScheme.count)))
self?.startButton.titleLabel!.textColor = CONSTANTS.MainColorScheme[randomIndex]
}) { (stuff Bool) -> Void in
}
This doesn't seem to work...it just "jumps" to the next color. It doesn't fade.
However, if I apply the same approach to self.view.backgroundColor, my code works.
Is there an alternative?

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;
}];

Indeed, the UIView.animateWithDuration will work with UILabel backgroundColor, while for the textColor it won't because color doesn't animate with UIView animations. Then, how to achieve?
Solution 1:
You can go for CATextLayer instead of a UILabel and then animate the color. For objective-C you can refer to this - iPad: Animate UILabels color changing
Solution 2:
You can use NSTimer and decrease the UILabel alpha as the time passes
Solution 3:
Or simply you can go for "FadeIn - FadeOut" animation with UIView.transitionWithView

Try this..
UIView.transitionWithView(YOURLABEL, duration: 0.325, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
// animation...
}, completion: { (fininshed: Bool) -> () in
// completion...
})
Also check for other syntax here
Cheers! :)

Related

iOS/Swift: Use UIView.animate to animate text getting added/removed from UITextView

I am adding and removing attributed text from a UITextView. I wish to use UIView.animate to add an animation to when text is appended to the text view and when that appended text is removed from the text view. So far, I have this, but it does not cause any noticeable animation on the text view:
UIView.animate(withDuration: 1, delay: 0, options: .CurveLinear, animations: {
self.view.layoutIfNeeded()
self.textView.attributedText = newAttributedText
}, completion: { finished in
print("Animation completed")
}
// prints "Animation completed", but no animation occurs
You cannot animate changing of text in that manner. There is a list of animatable properties of CALayer class and UIView class.
Set text cannot be animated by UIView.animate. Only changes like transparency, colors, shape, location, etc can be animated by the UIView.animate.
Here is an example of animating origin and transparency change with code looks like
self.textView.text = "test Animation"
self.textView.alpha = 0
UIView.animate(withDuration: 3, delay: 0, options: .curveLinear, animations: {
var frame = self.textView.frame
frame.origin.x = frame.origin.x + 50
frame.origin.y = frame.origin.y + 50
self.textView.frame = frame
self.textView.alpha = 1
}, completion: { finished in
print("Animation completed")
})
And animation looks like
Here is my solution (swift 3 ) :
let animator = UIViewPropertyAnimator(duration: 0.2, curve: .easeOut, animations: {
self.textView2.frame = self.textView2.frame.offsetBy(dx: 20 , dy: 25)
} )
animator.startAnimation()

Animate UILabel resizing from center?

I want to resize my label from center and animate that process. However, the only option I can use there is CGAffineTransformMakeScale with default anchor point of corresponding CALayer.
[UIView animateWithDuration:0.5 animations:^{
self.title.transform = CGAffineTransformMakeScale(2.0,1.0);
}];
But it squished the text inside. I considered this answer and it looks like the problem is solved in Swift.
However, I can't modify only size part of the frame. How to approach this in Obj C?
This will work for you. You just need to give scale for width and height (default 1, which will have no effect). And this will animate your view's frame and set it back to the original.
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.textLabel.transform=CGAffineTransformMakeScale(3, 1);
}
completion:^(BOOL finished){
self.textLabel.transform=CGAffineTransformIdentity;
}];
If you dont want revert effect than you can do like this
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.textLabel.transform=CGAffineTransformMakeScale(3, 1);
}
completion:nil];
From what I understood you want to change the size of UILabel not the text via animation. This can be achieved by grabbing a copy of the current label's frame and then modify it and set it back.
i.e
var frame = self.myLabel.frame
// frame modification ...
self.myLabel.frame = frame
Solution
Code
For the sizeTranformAction: you can a simple button and connect it to test the behavior.
class SizeTransformViewController: UIViewController {
var myLabel: UILabel! = nil
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.setupLabel()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Setups
func setupLabel() {
self.myLabel = UILabel()
self.myLabel.center = self.view.center
self.myLabel.text = "Text"
self.myLabel.sizeToFit()
self.myLabel.textAlignment = NSTextAlignment.Center
self.myLabel.backgroundColor = UIColor.redColor()
self.view.addSubview(self.myLabel)
}
// MARK: - Actions
#IBAction func sizeTranformAction(sender: AnyObject) {
var frame = self.myLabel.frame
let size = frame.size
frame.size = CGSize(width: 2*size.width, height: size.height)
frame.origin.x = self.view.center.x - size.width
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.myLabel.frame = frame
}, completion: nil)
}
}
Output
I actually ended up with 2 labels. Animation sequence is following:
Create a temporary label with no text in it, attributes like bg
color, font, font size, etc should be the same as of your main label.
Place the temporary label above your main label. Set main label's text property to nil
Animate the frame of main label to a desired value
When animation finishes, set back main label's text property to original value (presumably, in completion handler of animateWithDuration:)
Hide temporary label / remove it at all
You can change your constant to compensate the new width after scaling. Would this help?
layoutIfNeeded()
let scale: CGFloat = 0.72
let consLeft = NSLayoutConstraint(
item: titleLabel,
attribute: .left,
relatedBy: .equal,
toItem: self,
attribute: .left,
multiplier: 1,
constant: titleLabel.frameWidth * -(1-scale)/2
)
consLeft.isActive = true
let animations = {
self.titleLabel.transform = CGAffineTransform(scaleX: scale, y: scale)
self.layoutIfNeeded()
}
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseOut],
animations: animations,
completion: nil)

Animate rectangle by change width and height

I have ImageView in my UIViewController:
And i want to animate this Image, periodically change width and height. The same animation you can see at Apple Wallet app on Iphone, if you click "Scan code". I tried a lot, but my code working incorrect or app begin lagging after 3-5 min...
This is my last code:
class ViewController: UIViewController {
#IBOutlet weak var focusImage: UIImageView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.resizeFocusImageUp()
}
func resizeFocusImageUp() {
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.focusImage.frame = CGRectMake(
self.focusImage.frame.origin.x+50,
self.focusImage.frame.origin.y-50,
self.focusImage.frame.size.width-50,
self.focusImage.frame.size.height+50
)
self.view.layoutIfNeeded()
self.view.bringSubviewToFront(self.focusImage)
}, completion: {(Bool) in
self.resizeFocusImageDown()
})
}
func resizeFocusImageDown() {
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.focusImage.frame = CGRectMake(
self.focusImage.frame.origin.x-50,
self.focusImage.frame.origin.y+50,
self.focusImage.frame.size.width+50,
self.focusImage.frame.size.height-50
)
self.view.layoutIfNeeded()
self.view.bringSubviewToFront(self.focusImage)
}, completion: {(Bool) in
self.resizeFocusImageUp()
})
}
}
Also, for my ImageView i use constraints:
As the other poster said, if you're using AutoLayout then you need to animate the constraints, not the frame.
Create constraints for the height & width of your view, and position if you need to move it in your animation. Then control-drag from your CONSTRAINTS into the header of your view controller to create outlets.
Then in your animation block change the constant value of each constraint, then call layoutIfNeeded to trigger the animation. (The critical part is that the call to layoutIfNeeded needs to be inside the animation block. It seems that what actually causes the constraint to change the size/position of the view(s).)
All that being said, for a line drawing like you show in your post, you'd be much better off using a CAShapeLayer and animating the path of the layer. You'd get much crisper-looking shapes, and the animations are very smooth.
//Below Objective-C code working correctly in my app. Do your stuff as per your requirements.
Note: If you are using autoalyout for that object(Image view) then add below two line code in view did load method.
imageView.translatesAutoresizingMaskIntoConstraints = YES;
imageView.frame = CGRectMake(x, y, width, height);
//Method For Zoom IN Animations Of View
-(void)ZoomInAnimationOfView{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.01, 0.01);
[UIView animateWithDuration:1.0 animations:^{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
} completion:^(BOOL finished) {
imageView.transform = CGAffineTransformIdentity;
}];
}
//Method For Zoom Out Animations
-(void)ZoomOutAnimationOfView{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
[UIView animateWithDuration:1.0 animations:^{
imageView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
} completion:^(BOOL finished) {
imageView.transform = CGAffineTransformIdentity;
}];
}

Fading UILabel in from left to right

I want to animate a UILabel to fade text in from left to right. My code below fades the entire UILabel in at once, but I would like to go letter by letter or word by word. Also, words can be added to this text, and when that happens I only want to fade in the new words, not the entire UILabel again.
__weak ViewController *weakSelf = self;
weakSelf.label.alpha = 0;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn
animations:^{ weakSelf.label.alpha = 1;}
completion:nil];
I have done this once before with decent results. You will probably have to tweak it to your preference.
First step is to create a gradient image to match the way you want the text to fade in. If your text color is black you can try this.
- (UIImage *)labelTextGradientImage
{
CAGradientLayer *l = [CAGradientLayer layer];
l.frame = self.label.bounds;
l.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:0] CGColor], (id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:1] CGColor], nil];
l.endPoint = CGPointMake(0.0, 0.0f);
l.startPoint = CGPointMake(1.0f, 0.0f);
UIGraphicsBeginImageContext(self.label.frame.size);
[l renderInContext:UIGraphicsGetCurrentContext()];
UIImage *textGradientImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return textGradientImage;
}
That code creates an black image that should fade from left to right.
Next, when you create you label, you need to set its text color based from a pattern made from the image we just generated.
self.label.alpha = 0;
self.label.textColor = [UIColor colorWithPatternImage:[self labelTextGradientImage]];
Assuming that works, the last step is to animate the label into view and change its text color while that happens.
[UIView transitionWithView:self.label duration:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.label.textColor = [UIColor blackColor];
self.label.alpha = 1;
} completion:^(BOOL finished) {
}];
The animation code is a little funky because the UILabel's textColor property doesn't seem to be animatable with a UIViewAnimation.
Well, I know how I would do it. I would make a mask containing a gradient, and animate the mask across the front of the label.
Here's the result when I do that (don't know whether it's exactly what you have in mind, but perhaps it's a start):
I managed to achieve this and created a function within a UIView extension:
#discardableResult public func addSlidingGradientLayer(withDuration duration: Double = 3.0, animationDelegate delegate: CAAnimationDelegate? = nil) -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor, UIColor.clear.cgColor]
gradientLayer.startPoint = CGPoint(x: -1.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
let gradientAnimation = CABasicAnimation(keyPath: "startPoint")
gradientAnimation.fromValue = gradientLayer.startPoint
gradientAnimation.toValue = gradientLayer.endPoint
gradientAnimation.duration = duration
gradientAnimation.delegate = delegate
gradientLayer.add(gradientAnimation, forKey: "startPoint")
layer.mask = gradientLayer
return gradientLayer
}
To begin with we create a CAGradientLayer instance that we use as a mask for a view (in this case a UILabel).
We then create a CABasicAnimation instance, which we use to animate the startPoint of the CAGradientLayer. This has the effect of sliding the startPoint from left to right with some supplied duration value.
Finally, we an also the delegate of the CABasicAnimation. Then, if we adhere to the CAAnimationDelegate protocol, we can execute some code after the animation completes.
This is how you might use this extension:
class ViewController: UIViewController {
#IBOutlet weak var someLabel: UILabel!
private func configureSomeLabel() {
someLabel.text = "Sliding into existence!"
someLabel.sizeToFit()
someLabel.addSlidingGradientLayer(withDuration: 3.0, animationDelegate: self)
}
override func viewDidLoad() {
super.viewDidLoad()
configureSomeLabel()
}
}
Where someLabel has been hooked up to your storyboard. And as we've assigned this view controller as the animation delegate we need to add:
extension ViewController: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
someLabel.layer.mask = nil
}
}
where I have chosen to remove the masking layer.
Hope this helps someone!

iOS SDK: How to cause view to flip when switching cameras

Fairly new at iOS/Objective C. I'm making mods to Apple's AVCam (video capture) sample code and would like to imitate the native camera's flip animation when switching between front and back cameras. It seems as if this would be easy but I can't get a handle on how it's done. Advice would be welcome.
Thanks!
Mark
It is indeed a simple task. All you need to do is call the method transitionWithView from UIView right before changing the previewView input.
If you're targeting iOS 8.0 or later, you can also easily add a blur effect.
Swift 2
let blurView = UIVisualEffectView(frame: previewView.bounds)
blurView.effect = UIBlurEffect(style: .Light)
previewView.addSubview(blurView)
UIView.transitionWithView(previewView, duration: 0.4, options: .TransitionFlipFromLeft, animations: nil) { (finished) -> Void in
blurView.removeFromSuperview()
}
Swift 3, 4, 5
let blurView = UIVisualEffectView(frame: previewView.bounds)
blurView.effect = UIBlurEffect(style: .light)
previewView.addSubview(blurView)
UIView.transition(with: previewView, duration: 0.4, options: .transitionFlipFromLeft, animations: nil) { (finished) -> Void in
blurView.removeFromSuperview()
}
Objective-C
UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithFrame:_previewView.bounds];
blurView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
[_previewView addSubview:blurView];
[UIView transitionWithView:_previewView duration:0.4 options:UIViewAnimationOptionTransitionFlipFromLeft animations:nil completion:^(BOOL finished) {
[blurView removeFromSuperview];
}];

Resources