Reputation: 10333
I'm trying to create a custom "blinking cursor" in UIKit, I've tried as shown below, having 2 functions that basically keep calling each other until the cursor is hidden. But this leads to a nice infinite recursion... for some reason the functions call each other right away, not each half-second as expected.
I tried returning if the 'finished' parameter is not YES (by uncommenting the 'if (!ok)' line), but that leads to no animation at all...
Any better idea? Did I miss something, is there a much-easier way to make a "blinking cursor"?
- (void)onBlinkIn:(NSString *)animationID finished:(BOOL)ok context:(void *)ctx {
if (cursorView.hidden) return;
//if (!ok) return;
[UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(onBlinkOut:finished:context:)];
cursorView.textColor = [UIColor grayColor];
[UIView commitAnimations];
}
- (void)onBlinkOut:(NSString *)animationID finished:(BOOL)ok context:(void *)ctx {
if (cursorView.hidden) return;
[UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(onBlinkIn:finished:context:)];
cursorView.textColor = [UIColor clearColor];
[UIView commitAnimations];
}
Upvotes: 19
Views: 13956
Reputation: 4862
Matt Long's answer in Swift 4:
func addOpacityAnimation(view: UIView) {
let key = "opacity"
let animation = CABasicAnimation(keyPath: key)
animation.fromValue = 1.0
animation.toValue = 0.0
animation.duration = 0.5
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.autoreverses = true
animation.repeatCount = Float.greatestFiniteMagnitude
view.layer.add(animation, forKey: key)
}
Upvotes: 5
Reputation: 789
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"opacity";
animation.values = @[ @(0.0),@(0.0),@(1.0), @(1.0)];
animation.keyTimes = @[ @(0.001),@(0.49),@(0.50), @(1.0)];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.duration = 1.2;
animation.autoreverses = NO;
animation.repeatCount= HUGE;
[layer addAnimation:animation forKey:@"blink"];
Upvotes: 0
Reputation: 24466
Do it the Core Animation way:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setFromValue:[NSNumber numberWithFloat:1.0]];
[animation setToValue:[NSNumber numberWithFloat:0.0]];
[animation setDuration:0.5f];
[animation setTimingFunction:[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear]];
[animation setAutoreverses:YES];
[animation setRepeatCount:20000];
[[view layer] addAnimation:animation forKey:@"opacity"];
Where view is the UIView you want to blink. Core Animation makes this very convenient because it will auto reverse the animation for you. Keep in mind that your complete duration is double what you set in the duration field because the value you specify applies to the forward direction. If you want the whole animation to run (forward and then back) in the specified duration, split the duration in half.
Upvotes: 47
Reputation: 32316
On the delegate:
- (void)blinkAnimation:(NSString *)animationId finished:(BOOL)finished target:(UIView *)target
{
if (shouldContinueBlinking) {
[UIView beginAnimations:animationId context:target];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(blinkAnimation:finished:target:)];
if ([target alpha] == 1.0f)
[target setAlpha:0.0f];
else
[target setAlpha:1.0f];
[UIView commitAnimations];
}
}
And to start the animation:
shouldContinueBlinking = YES;
[self blinkAnimation:@"blinkAnimation" finished:YES target:cursorView];
Also, ensure your class has a shouldContinueBlinking instance variable
Upvotes: 16
Reputation: 162712
Most likely, you are blocking the main event loop and, thus, blocking the animations when you try to only animate on finish.
Instead, set up a timer that fires after 1/2 a second that kicks off the next animation. That timer could be reset on the finish of the previous animation, thus reducing load and making your blink rate a bit more regular (but you'll have to figure out what is most appropriate).
See the NSTimer class's documentation.
Note that any kind of constant animation like this will put a drain on the battery. Not a huge one, by any means, but... still...
Upvotes: 0