iOS CAKeyFrameAnimation Scaling在动画结束时闪烁

Pet*_*r M 6 scaling flicker uiimageview cakeyframeanimation ios

在关键帧动画的另一个测试中,我正在组合theImage沿着贝塞尔曲线路径移动UIImageView(被调用)并在移动时缩放它,导致路径末端的图像大2倍.我这样做的初始代码中包含以下元素来启动动画:

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"];
Run Code Online (Sandbox Code Playgroud)

然后,当动画完成时我想保留更大尺寸的图像,所以我实现:

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

这一切都有效..有点儿.问题在于动画结束时会theImage短暂闪烁 - 足以让它看起来很糟糕.我猜这是动画结束时的过渡,我将变换设置为新的大小.

在试验这个时,我尝试了上面稍微不同的形式,但仍然有相同的闪烁:

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);
Run Code Online (Sandbox Code Playgroud)

但当我以与原版相同的大小结束动画时,没有闪烁:

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);
Run Code Online (Sandbox Code Playgroud)

所以我的大问题是如何在没有闪烁的情况下为此图像设置动画,并在动画结束时以不同的大小结束?


编辑3月2日

我最初的测试是缩放图像.我只是尝试缩小它(IE scaleFactor = 0.4)并且闪烁更加明显,而且我所看到的更加明显.这是一系列事件:

  1. 原始尺寸的图像在起始位置的屏幕上绘制.
  2. 随着图像沿着路径移动,它会平滑地收缩.
  3. 完全收缩的图像到达路径的末尾.
  4. 然后以原始尺寸绘制图像.
  5. 图像最终以缩小的尺寸绘制.

所以似乎第4步就是我所看到的闪烁.


编辑3月22日

我刚刚向GitHub上传了一个演示项目,该项目展示了沿着bezier路径移动对象的过程.代码可以在PathMove找到

我还在我的博客中写了一篇关于在iOS中沿着bezier路径移动对象的文章

rob*_*off 7

使用Core Animation为视图的图层设置动画可能会很棘手.有几件事令人困惑:

  • 在图层上设置动画不会更改图层的属性.相反,它会更改"表示层"的属性,只要应用动画,它就会替换屏幕上的原始"模型层".

  • 更改图层的属性通常会向图层添加隐式动画,并将属性名称作为动画的键.因此,如果要显式动画属性,通常需要将属性设置为其最终值,然后添加其键为属性名称的动画,以覆盖隐式动画.

  • 视图通常会在其图层上禁用隐式动画.它还以其他有些神秘的方式在其层的属性上进行修复.

另外,让视图的边界变焦以进行缩放是令人困惑的,但最后切换到缩放变换.

我认为最简单的方法就是尽可能地使用UIView动画方法,并且只为关键帧动画引入Core Animation.在您UIView添加自己的动画后,可以将关键帧动画添加到视图的图层,并且关键帧动画将覆盖添加的动画UIView.

这对我有用:

- (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];
    }];
}
Run Code Online (Sandbox Code Playgroud)