iOS的CAKeyFrameAnimation缩放在animation结束的闪烁

在关键帧animation的另一个testing中,我将沿着theImagepath移动UIImageView(称为theImage ),并在移动时将其放大,从而在path的末端产生两倍大的图像。 我最初的代码是通过这些元素来启动animation:

 UIImageView* theImage = .... float scaleFactor = 2.0; .... theImage.center = destination; theImage.transform = CGAffineTransformMakeScale(1.0,1.0); CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"]; [resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor)]]; resizeAnimation.fillMode = kCAFillModeBackwards; resizeAnimation.removedOnCompletion = NO; CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.path = [jdPath path].CGPath; pathAnimation.fillMode = kCAFillModeBackwards; pathAnimation.removedOnCompletion = NO; CAAnimationGroup* group = [CAAnimationGroup animation]; group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil]; group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; group.removedOnCompletion = NO; group.duration = duration; group.delegate = self; [theImage.layer addAnimation:group forKey:@"animateImage"]; 

然后,当animation完成时,我想保留更大的图像,所以我执行:

 - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor); } 

这一切工作..有点。 问题是,在animation结束时,图像会在短暂的时间内闪烁 – 这足以使其看起来很糟糕。 我猜测,这是在animation结束时的转换,我将转换设置为新大小。

在这个实验中,我尝试了一个稍微不同的forms,但仍然有同样的闪烁:

 CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)]; NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)]; NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, endSizeKey, nil]; [resizeAnimation setValues:sizeKeys]; .... theImage.transform = CGAffineTransformMakeScale(scaleFactor,scaleFactor); 

但是,当我结束与原来相同的大小的animation,没有闪烁:

 CAKeyframeAnimation *resizeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; NSValue* startSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)]; NSValue* middleSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, scaleFactor, scaleFactor, 1.0)]; NSValue* endSizeKey = [NSValue valueWithCATransform3D:CATransform3DScale (theImage.layer.transform, 1.0, 1.0, 1.0)]; NSArray* sizeKeys = [NSArray arrayWithObjects:startSizeKey, middleSizeKey, endSizeKey, nil]; [resizeAnimation setValues:sizeKeys]; .... theImage.transform = CGAffineTransformMakeScale(1.0,1.0); 

所以我最大的问题是如何在没有闪烁的情况下为这个图像制作animation,并在animation结束时以不同的大小结束?


编辑3月2日

我最初的testing是缩放图像。 我只是尝试缩小它(IE scaleFactor = 0.4),闪烁是更明显,更明显的是我所看到的。 这是一连串的事件:

  1. 原始大小的图像被绘在起始位置的屏幕上。
  2. 当图像沿着path移动时,它会平滑地收缩。
  3. 完全缩小的图像到达path的尽头。
  4. 图像然后被绘制在它的原始大小。
  5. 最后画的图像缩小了。

所以看来是第四步,就是我所看到的闪烁。


3月22日编辑

我刚刚上传到GitHub上的一个演示项目,展示了一个沿着贝塞尔path移动的对象。 代码可以在PathMove中find

我还在我的博客中写过关于在iOS中沿着贝塞尔path移动对象的问题

使用Core Animation对视图的图层进行animation处理可能会非常棘手。 有几件事令人困惑:

  • 在图层上设置animation不会改变图层的属性。 相反,只要应用animation,它就会更改“表示层”的属性,以replace屏幕上的原始“模型层”。

  • 更改图层的属性通常会向图层添加一个隐式animation,并将属性名称作为animation的关键字。 因此,如果要显式地animation属性,通常需要将该属性设置为其最终值, 然后添加其属性为属性名称的animation以覆盖隐式animation。

  • 视图通常禁用其图层上的隐式animation。 它也用其他有些神秘的方式来弥补其图层的属性。

另外,让视图的边界animation来扩展它也是令人困惑的,但是最后切换到缩放转换。

我认为最简单的方法是尽可能地使用UIViewanimation方法,并且只为关键帧animation引入Core Animation。 在让UIView添加自己的animation之后,您可以将关键帧animation添加到视图的图层,而关键帧animation将覆盖由UIView添加的animation。

这对我工作:

 - (IBAction)animate:(id)sender { UIImageView* theImage = self.imageView; CGFloat scaleFactor = 2; NSTimeInterval duration = 1; UIBezierPath *path = [self animationPathFromStartingPoint:theImage.center]; CGPoint destination = [path currentPoint]; [UIView animateWithDuration:duration animations:^{ // UIView will add animations for both of these changes. theImage.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor); theImage.center = destination; // Prepare my own keypath animation for the layer position. // The layer position is the same as the view center. CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; positionAnimation.path = path.CGPath; // Copy properties from UIView's animation. CAAnimation *autoAnimation = [theImage.layer animationForKey:@"position"]; positionAnimation.duration = autoAnimation.duration; positionAnimation.fillMode = autoAnimation.fillMode; // Replace UIView's animation with my animation. [theImage.layer addAnimation:positionAnimation forKey:positionAnimation.keyPath]; }]; } 

我本周正在尝试一些CAAnimations,并注意到在animation结尾处闪烁。 特别是,我将animation从一个圆形到一个正方形,同时改变fillColor以及。

每个CAAnimation都有一个名为removedOnCompletion的属性,默认为YES。 这意味着当animation完成时,animation将消失(即转换,缩放,旋转等),并且您将留下原始图层。

既然你已经把你的removedOnCompletion属性设置为NO,我会build议尝试将你的animation的执行转移到使用CATransactions,而不是委托和animationDidStop …

 [CATransaction begin]; [CATransaction setDisableActions:YES]; [CATransaction setCompletionBlock: ^{ theImage.transform = ...}]; // ... CAAnimation Stuff ... // [CATransaction commit]; 

在创buildanimation之前,请先完成事务的完成块调用,如下所示:http: //zearfoss.wordpress.com/2011/02/24/core-animation-catransaction-protip/

以下是我的一个方法:

 [CATransaction begin]; CABasicAnimation *animation = ...; animation.fromValue = ...; animation.toValue = ...; [CATransaction setCompletionBlock:^ { self.shadowRadius = _shadowRadius; }]; [self addAnimation:animation forKey:@"animateShadowOpacity"]; [CATransaction commit]; 

而且,我构build了这个animation,并且对我来说工作正常,最后没有任何小故障:设置和触发器是我在窗口中的自定义方法,然后在mousedown上触发animation。

 UIImageView *imgView; UIBezierPath *animationPath; -(void)setup { canvas = (C4View *)self.view; imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"img256.png"]]; imgView.frame = CGRectMake(0, 0, 128, 128); imgView.center = CGPointMake(384, 128); [canvas addSubview:imgView]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [UIImageView animateWithDuration:2.0f animations:^{ [CATransaction begin]; CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.duration = 2.0f; pathAnimation.calculationMode = kCAAnimationPaced; animationPath = [UIBezierPath bezierPath]; [animationPath moveToPoint:imgView.center]; [animationPath addLineToPoint:CGPointMake(128, 512)]; [animationPath addLineToPoint:CGPointMake(384, 896)]; pathAnimation.path = animationPath.CGPath; pathAnimation.fillMode = kCAFillModeForwards; pathAnimation.removedOnCompletion = NO; [imgView.layer addAnimation:pathAnimation forKey:@"animatePosition"]; [CATransaction commit]; CGFloat scaleFactor = 2.0f; CGRect newFrame = imgView.frame; newFrame.size.width *= scaleFactor; newFrame.size.height *= scaleFactor; newFrame.origin = CGPointMake(256, 0); imgView.frame = newFrame; imgView.transform = CGAffineTransformRotate(imgView.transform,90.0*M_PI/180); }]; } 

如果terminal状态的分配方式使得它自己创build了一个隐含的animation,则CAAnimation将在最后闪烁。 请记住, CAAnimation是为了可视化转换而临时调整对象属性。 当animation完成时,如果图层的状态仍然是原始的开始状态,那么这将暂时显示,直到您设置最后一个图层状态,您在animationDidStop:方法中执行此操作。

此外,您的animation正在调整图层的bounds.size属性,因此您应该类似地设置最终状态,而不是使用变换调整作为最终状态。 您也可以使用transform属性作为animation中的animation属性而不是bounds.size

为了弥补这一点,在分配animation之后立即将图层的渗透性状态改变为您想要的terminal状态,以便animation完成时不会有闪烁,但是这样做是为了在animation开始之前不触发隐式animation。 具体来说,在你的情况下,你应该在你的animation设置结束时做到这一点:

 UIImageView* theImage = .... float scaleFactor = 2.0; .... theImage.center = destination; theImage.transform = CGAffineTransformMakeScale(1.0,1.0); CGSize finalSize = CGSizeMake(theImage.image.size.height*scaleFactor, theImage.image.size.width*scaleFactor); CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"]; [resizeAnimation setToValue:[NSValue valueWithCGSize:finalSize]]; resizeAnimation.fillMode = kCAFillModeBackwards; resizeAnimation.removedOnCompletion = NO; CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.path = [jdPath path].CGPath; pathAnimation.fillMode = kCAFillModeBackwards; pathAnimation.removedOnCompletion = NO; CAAnimationGroup* group = [CAAnimationGroup animation]; group.animations = [NSArray arrayWithObjects:pathAnimation, resizeAnimation, nil]; group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; group.removedOnCompletion = NO; group.duration = duration; group.delegate = self; [theImage.layer addAnimation:group forKey:@"animateImage"]; [CATransaction begin]; [CATransaction setDisableActions:YES]; theImage.bounds = CGRectMake( theImage.bounds.origin.x, theImage.bounds.origin.y, finalSize.width, finalSize.height ); [CATransaction commit]; 

然后删除animationDidStop:方法中的变换调整。