如何在iPhone上制作一个闪烁的光标(或闪烁的光标)?

19

我正在尝试在UIKit中创建一个自定义的“闪烁光标”,我已经尝试如下方式,有两个函数互相调用,直到光标隐藏。但是这会导致一个漂亮的无限递归……由于某种原因,这些函数立即互相调用,而不是每半秒一次。

我尝试了返回值如果'finished'参数不为YES(取消对'if (!ok)'行的注释),但这导致根本没有动画......

有更好的想法吗?我有遗漏什么吗?是否有更简单的方法制作“闪烁光标”?

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

context参数是一个void指针,而不是CGContextRef(虽然传递CGContextRef是有效的,但肯定没有用) - rpetrich
5个回答

47

按照核心动画的方式来实现:

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

在Core Animation中,想要让UIView闪烁,只需要设置好需要闪烁的view即可。Core Animation非常方便,因为它会自动为您反转动画。请注意,您设置的完整时间是正向时间的两倍,因为您指定的值适用于正向方向。如果您希望整个动画在指定的时间内运行(正向和反向),请将持续时间分成两半。


太好了!我的应用程序中没有QuartzCore框架,也不需要它,但这是一个非常好的样例,我会保留它以备将来编程之用。比“UIView方式”更复杂(我觉得)。 - Zoran Simic
2
你应该使用HUGE_VALF而不是20000。然后它会一直重复。 - José Manuel Sánchez
2
@josema.vitaminew 我只想让它重复20000次。无限制会太多了。;-) - Matt Long

16

关于委托:

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

接着开始动画:

shouldContinueBlinking = YES;
[self blinkAnimation:@"blinkAnimation" finished:YES target:cursorView];

另外,请确保您的类具有 shouldContinueBlinking 实例变量。


2
另外,设置alpha而不是textColor将允许GPU执行所有绘图操作,并提高性能。同样,将cursorView设置为UIView并设置背景颜色将比使用UILabel(远比使用UITextView或UITextField)使用更少的RAM和CPU。 - rpetrich
太棒了!这比我原本想的要好得多,而且易于重复使用。 - Zoran Simic
从beginAnimations:context:的文档中可以看出:“在iOS 4.0及更高版本中,不建议使用此方法。相反,您应该使用基于块的动画方法来指定动画。” - drewish
@drewish:这个答案是在iOS4.0发布之前写的。两个选项肯定都是有效的,Matt Long的CoreAnimation版本也是如此。 - rpetrich
@rpetrich 当然,我留下这个注释只是为了让人们意识到它已经被弃用。Stack Overflow 最好和最坏的一点就是它的历史深度,好处是几乎总能找到解决问题的线索,但坏处是流行的答案通常使用过时的技术。 - drewish

5

Matt Long在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)
}

0
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"];

0

很可能,当你尝试只在完成时进行动画时,你会阻塞主事件循环,从而阻塞动画。

相反,设置一个定时器,在半秒后触发下一个动画。该定时器可以在上一个动画完成时重置,从而减少负载并使您的闪烁速率更加规则(但您必须确定什么是最合适的)。

请参阅NSTimer类的文档。

请注意,任何类型的持续动画都会对电池造成一定的耗损。虽然不是很大,但还是要注意。


动画通常不会持续很长时间,只是足够让用户使用自定义键盘输入数字的时间...屏幕上有几个数字,一个轻柔的闪烁光标应该能够清晰地显示正在编辑的数字(该数字位于自定义视图中,而不是文本字段中)。我认为对电池的影响将是可以忽略不计的(因为时间很短)。 - Zoran Simic
是的 - 我提到电池寿命只是因为它是移动平台上一个有趣的额外考虑因素。 - bbum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接