我最终试图想出这个范例的设计模式。我在这个网站上没有太多运气,但在这个阶段我会尝试任何事情。
我正在尝试实现雷达类型动画,因此我将视图旋转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,再次调用动画方法以及检查与点的交集。这也很有效,但很像第三种解决方案,当滚动和其他动画发生时,它是生涩的。这很可能是由动画的停止启动性质引起的。
基本上,有没有办法无限制地动画视图但是在每个重复循环后调用一个方法?或者有没有办法让第三个解决方案更顺畅地运作?
请帮助,我已经完成了设计模式。
答案 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];
这个想法是每次完成雷达活动后再次调用动画序列,假设雷达检查不是很重,它应该是光滑的。