使用 UIImageView 动画创建流畅的动画

cod*_*man 4 objective-c ios

我目前正在 4 张图像之间制作动画,如下所示:

UIImageView *tom3BeforeImage;
tom3Images = [NSArray arrayWithObjects: [UIImage imageNamed:@"floortom_before1.png"],[UIImage imageNamed:@"floortom_before2.png"],[UIImage imageNamed:@"floortom_before3.png"],[UIImage imageNamed:@"floortom_before4.png"], nil ];
tom3BeforeImage.animationImages = tom3Images;
tom3BeforeImage.animationDuration = 0.75;
[tom3BeforeImage startAnimating];
Run Code Online (Sandbox Code Playgroud)

它工作正常,除了图像之间的动画断断续续。我需要的持续时间正好是 0.75 秒,所以加快速度不是一种选择。

让动画在图像之间更平滑的最佳方法是什么,有点像在每个图像变化之间进行混合?

谢谢!

Dun*_*n C 5

如果您使用基于帧的 UIImageView 动画,并且动画必须为 0.75 秒,那么我所知道的使其更流畅的唯一方法是创建更多帧。尝试 30 帧/秒,或大约 22 帧。那应该给出非常平滑的运动。

如果您想在帧之间进行某种交叉融合,那么您将无法使用 UIView 帧动画。你将不得不使用 UIView 块动画(使用animateWithDuration:animations:或其表亲。)

您可以在帧之间创建一系列交叉溶解,其中该序列的总持续时间为 0.75 秒。让每个转换触发它的完成块中的下一个转换。

像这样的东西:

您需要 2 个图像视图,彼此堆叠在一起。您将同时淡出另一个。您需要在两者上将 opaque 标志设置为 NO。

让我们称它们为 tom3BeforeImage1 和 tom3BeforeImage2

添加一个 int 实例变量 imageCount 并创建图像数组、tom3Images 和实例变量:

- (void) animateImages;
{
  CGFloat duration = .75 / ([tom3Images count] -1);

  //Start with the current image fully visible in tom3BeforeImage1
  tom3BeforeImage1.image = tom3Images[imageCount];
  tom3BeforeImage1.alpha = 1.0;

  //Get the next image ready, at alpha 0, in tom3BeforeImage2
  tom3BeforeImage2.image = tom3Images[imageCount+1];
  tom3BeforeImage2.alpha = 0;
  imageCount++

  [UIView animateWithDuration: duration  
    delay: 0 
    options: UIViewAnimationOptionCurveLinear
    animations:
    ^{
      //Fade out the current image
       tom3BeforeImage1.alpha = 0.0;

       //Fade in the new image
       tom3BeforeImage2.alpha = 1.0;
    }
    completion:
    ^{
    //When the current animation step completes, trigger the method again.
    if (imageCount < [tom3Images count] -1)
       [self animateImages];
    }
  ];
}
Run Code Online (Sandbox Code Playgroud)

请注意,我在没有喝足够咖啡的情况下在论坛编辑器中敲出了上面的代码。它可能包含语法错误,甚至可能有逻辑问题。这只是让你思考如何去做。

编辑#2:

我不知道为什么,但我决定把它充实到一个完整的示例项目中。上面的代码在调试后运行良好,但由于它同时淡出一幅图像,同时淡入另一幅图像,因此两个图像视图背后的背景都显示出来了。

我对其进行了重新设计,使其具有仅淡入淡出顶部图像的逻辑。它将第一帧放在顶部图像视图中,将第二帧放在底部图像视图中,然后淡出顶部图像视图。

该项目位于 github 上,名为Animate-Img。(关联)

然后它在顶部图像视图中安装第三帧并将其淡入,

然后它在底部图像视图中安装第四个成名并淡出顶部以暴露底部等。

我最终创建了一个通用方法

- (void) animateImagesWithDuration: (CGFloat) totalDuration
                           reverse: (BOOL) reverse
                         crossfade: (BOOL) doCrossfade
               withCompletionBlock: (void (^)(void)) completionBlock;
Run Code Online (Sandbox Code Playgroud)

它会将一组图像动画化为一对图像视图,完成后可以选择反转动画。它需要一个完成块,一旦动画完成就会被调用。

动画按钮实际上调用了一个重复整个动画序列的方法。它当前设置为仅运行一次,但如果需要,更改常量将使程序重复整个序列。


ano*_*eal 5

我确实需要有包含图像数组的动画。最初,当我使用animationImagesimageview 的属性时,我得到了所需的动画,但图像之间的过渡不平滑,然后我用它CAKeyframeAnimation来实现平滑过渡,问题是timingFunction与正确的calculationMode. 我不确定这是否是问题的确切答案,但这是使动画更平滑的一种方法,\n 下面是该代码

\n\n

有关计算模式的更多信息,请参阅Apple 文档

\n\n
- (void) animate\n{\n    NSMutableArray * imageArray = [[NSMutableArray alloc] init];\n\n    int imageCount = 8;\n\n    for (int i=0; i<=imageCount; i++) {\n        [imageArray addObject:(id)[UIImage imageNamed:[NSString stringWithFormat:@\xe2\x80\x9cimage%d\xe2\x80\x9d,i]].CGImage];\n    }\n\n    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];\n    animation.calculationMode = kCAAnimationLinear;// Make sure this is kCAAnimationLinear or kCAAnimationCubic other mode doesnt consider the timing function\n    animation.duration = 8.0;\n    animation.values = imageArray;\n    animation.repeatCount = 1; // Change it for repetition\n    animation.removedOnCompletion = NO;\n    animation.fillMode = kCAFillModeForwards; // To keep the last frame when animation ends\n    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];\n    [imageView.layer addAnimation:animation forKey:@"animation"];\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新 - Swift 3

\n\n
func animate() {\n    var imageArray = [CGImage]()\n    let imageCount: Int = 3\n    for i in 0...imageCount {\n        imageArray.append((UIImage(named: String(format:"image\\(i)"))?.cgImage!)!)\n    }\n    let animation = CAKeyframeAnimation(keyPath: "contents")\n    animation.calculationMode = kCAAnimationLinear\n    // Make sure this is kCAAnimationLinear or kCAAnimationCubic other mode doesnt consider the timing function\n    animation.duration = CFTimeInterval(floatLiteral: 8.0)\n    animation.values = imageArray\n    animation.repeatCount = 1\n    // Change it for repetition\n    animation.isRemovedOnCompletion = false\n    animation.fillMode = kCAFillModeForwards\n    // To keep the last frame when animation ends\n    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)\n    animImageView.layer.add(animation, forKey: "animation")\n}\n
Run Code Online (Sandbox Code Playgroud)\n