UIView无限循环动画在每个重复循环后调用一个方法

时间:2010-10-12 23:32:52

标签: iphone core-animation ios4 quartz-graphics

我最终试图想出这个范例的设计模式。我在这个网站上没有太多运气,但在这个阶段我会尝试任何事情。

我正在尝试实现雷达类型动画,因此我将视图旋转360度以表示围绕圆圈旋转的半径。我在这个圆周围放置了点,并且能够使用标准的三角形计算圆心的角度。

当半径围绕圆圈扫过时,如果它与一个点相交(例如,该点的角度等于扫描半径的角度+公差),则该点会闪烁。

现在,我已经通过多种方式接近这个范​​例,但还没有达到理想的解决方案。这是我尝试过的。

FIRST:iOS4动画块。我将半径10度旋转,持续时间为.1秒,在完成方法中,检查与点的交点。这里的问题是,如果将选项设置为repeat,则不会调用completion方法。调用完成方法的唯一时间是整个动画终止时,而不是每个重复周期后,所以此解决方案不起作用。

SECOND:使用CABasicAnimation尝试显式动画。与上述方法相同,每0.1秒旋转10度并将委托设置为self并实现animationDidFinish方法。在animationDidFinish方法中,我检查了带点的交叉点。我将动画设置为累积,将repeatCound设置为巨大值。 raduis旋转但是再一次,除非我将repeatCount设置为较小的数字,然后只在repeatCount结束时调用,而不是在每个重复循环之后调用,否则不会调用animationDidFinish。

第三:使用NSTimer并且这种方法实际上可行,但动画可能会生涩,具体取决于屏幕周围发生的事情。我使用.1秒的计时器刻度并启动10度的单个动画以及每个刻度处的交叉点检查。这种方法的问题在于,由于处理器在后台执行其他动画,因此在开始每个动画时很容易被阻止。请注意,我没有遇到其他两种方法的问题!

第四:我尝试使用两者的组合。半径上的CABasicAnimation和点上的NSTimer。这个问题是,如果iDevice进入休眠状态然后恢复,它们可能会非常容易地发生。

第五:使用iOS3.0风格的动画块并旋转10度,持续时间为.1秒。使用animationDidStop方法设置委托和animationDidStopSelector,再次调用动画方法以及检查与点的交集。这也很有效,但很像第三种解决方案,当滚动和其他动画发生时,它是生涩的。这很可能是由动画的停止启动性质引起的。

基本上,有没有办法无限制地动画视图但是在每个重复循环后调用一个方法?或者有没有办法让第三个解决方案更顺畅地运作?

请帮助,我已经完成了设计模式。

3 个答案:

答案 0 :(得分:2)

让它完美运作。

这是我做的:

1 /将CABasicAnimation旋转对象应用于图层,每2秒旋转360度。 (不需要累积那种方式)

2 /设置一个计时器,每隔0.1秒从图层调用一个表示层。

3 /计算表示层中存储的3D变换的角度。 Click here关于如何执行此操作的答案。

4 /检查交叉角度。

完成: - )

值得注意的事项: 3D变换基于180和-180度之间的角度而不是0到360度 使用atan2而不是atan,因为后者只会在第一象限中给出角度。

答案 1 :(得分:1)

好的,自从我发布这个问题以来,我已经走了很长的路。

公认的设计模式是实际生成时钟节拍(通常使用CADisplayLink),以每秒30帧或60帧的速度运行,具体取决于您希望动画的平滑和快速程度。在每个帧回调中,您修改视图/图层上的变换并使用事务来显示它。

一些重要说明: 构建一个测试相交线和点的方法:

-(BOOL)intersectWithAngle:(CGFloat)rad {        
    return (rad <= angle && rad >= angle - angleIncrement);
}

尝试修改可设置动画的属性时,必须使用事务,以防止图层/视图为新值本身设置动画。 (你不想动画两次)

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

self.transform = rotationTransform;

[CATransaction commit];

无需每帧都继续创建新的转换,这只是浪费并消耗资源。 Best创建一个名为rotationTransform的实例变量/属性,并修改每一帧并将其重新应用于图层/视图。

-(void)displayFrame {   
    rotationTransform.a = cos(angle);
    rotationTransform.b = sin(angle);
    rotationTransform.c = -sin(angle);
    rotationTransform.d = cos(angle);

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

    self.transform = rotationTransform;

    [CATransaction commit];
}

接下来,创建一个实际设置角度的方法,使用实例变量/属性来存储角度并定义角度增量(我使用3度),也预定义2-pi,这样你就不会总是重新计算它

-(void)processFrame {
    angle += angleIncrement;
    if (angle >= M_2PI) {
        angle -= M_2PI;
    }

    if ([self intersectWithAngle:point.angle]) {
        [point animate];
    } 
}

最后,构建由CADisplayLink调用的方法,该方法设置框架并在保持帧同步的同时显示它。

-(void)displayLinkDidTick {
    // get the time now
    NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];

    if (timeOfLastDraw == 0) {
        numberOfTicks = 1;
    } else {
        NSTimeInterval timeSinceLastDraw = timeNow - timeOfLastDraw;
        NSTimeInterval desiredTimeInterval = kFrameDuration;

        numberOfTicks = (NSUInteger)(timeSinceLastDraw / desiredTimeInterval);      
    }

    if (numberOfTicks > 4) {
        numberOfTicks = 4;
        timeOfLastDraw = timeNow;
    } else {
        timeOfLastDraw += numberOfTicks * kFrameDuration;
    }


    while(numberOfTicks--) {

        [self processFrame];
        if (spriteState == SpriteStateIdle)
            break;
        }

    [self displayFrame];

}

此帖子的代码提取已经过大量修改,实际上我在同一个CADisplayLink实例中执行扫描线动画和点的动画。

答案 2 :(得分:0)

我想知道这是否有效:

UIView* yourView = ...;
// preparing the callbacks:
void(^)(void) animations = ...; // the animation would rotate 10 degrees
void(^)(BOOL) completion = ^(BOOL finished) {
  // do your radar stuff
  [UIView animateWithDuration:0.1 animations:animations completion:completion];
}

// the first call to start it all
[UIView animateWithDuration:0.1 animations:animations completion:completion];

这个想法是每次完成雷达活动后再次调用动画序列,假设雷达检查不是很重,它应该是光滑的。