如何在UIImageView中实现图像变化的动画效果?

229

我有一个带有图像的 UIImageView。现在我有一个全新的图片(图形文件),想要在这个 UIImageView 中显示它。如果我只是设置

myImageView.image = newImage;

新图像立即可见。不能进行动画。

我希望它能够平滑地淡入新图像。我想也许有比在其上创建一个新的UIImageView并使用动画混合的更好的解决方案?


https://dev59.com/9msz5IYBdhLWcg3w0rRC#38350024 - Kumar KL
15个回答

384
[UIView transitionWithView:textFieldimageView
                  duration:0.2f
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    imageView.image = newImage;
                } completion:nil];

还有另一种可能性。


1
简单并且可以打开图像间的一整个系列其他动画效果。 - So Over It
1
存在与@hfossli提到的帧改变相关的相同问题。如果在此期间更改布局,则动画会变得缓慢。 - Geva
1
这是最好的解决方案,因为它可以与其他动画结合使用。 - kelin

264

我不确定是否可以使用淡入淡出效果来动画化UIView,因为所有支持的视图转换似乎都在UIViewAnimationTransition枚举中定义。可以使用CoreAnimation实现渐变效果。以下是一个示例:

#import <QuartzCore/QuartzCore.h>
...
imageView.image = [UIImage imageNamed:(i % 2) ? @"3.jpg" : @"4.jpg"];

CATransition *transition = [CATransition animation];
transition.duration = 1.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;

[imageView.layer addAnimation:transition forKey:nil];

2
你的代码在从一张图片到另一张图片时运行得非常完美。 你知道如何让它与UIImageView的自动图像动画(animationImages属性)一起工作吗? - CedricSoubrie
你可以尝试使用CAKeyFrameAnimation来控制图层的内容属性,但不确定效果是否相同。或者设置动画代理,在代理回调函数中,在前一个动画完成时开始对下一张图片进行动画处理。 - Vladimir
2
这绝对比手动淡入淡出单独的图像视图简单。谢谢! - Kevlar
如果ImageView的框架改变,它仍然保持在原来的位置,这是个致命缺陷! - hfossli
@Fossli,你是说当帧动画变化时?那就是另一个问题了。 - Vladimir

204

用迈克尔·斯科特的话说,保持简单,傻瓜式的思路。以下是在Swift 3Swift 4中实现此目标的简单方法:

UIView.transition(with: imageView,
                  duration: 0.75,
                  options: .transitionCrossDissolve,
                  animations: { self.imageView.image = toImage },
                  completion: nil)

5
运作得很好,谢谢! - RobertoAllende
3
哇,太棒了!我竟然这么长时间都不知道这个,该死 :) - sabiland
4
这将变成1个图像。如果我有一组图像并且想要用过渡动画逐个显示它们怎么办? - Ammar Mujeeb
3
您可以使用完成回调来过渡到下一个步骤。 - Zia
2
最佳解决方案在这里! - user3788747

44

这里提供一个使用Swift的示例,首先会淡入一个新的图片,然后添加一个弹性动画:

var selected: Bool {
  willSet(selected) {
    let expandTransform:CGAffineTransform = CGAffineTransformMakeScale(1.15, 1.15);
    if (!self.selected && selected) {
      UIView.transitionWithView(self.imageView,
        duration:0.1,
        options: UIViewAnimationOptions.TransitionCrossDissolve,
        animations: {
          self.imageView.image = SNStockCellSelectionAccessoryViewImage(selected)
          self.imageView.transform = expandTransform
        },
        completion: {(finished: Bool) in
          UIView.animateWithDuration(0.4,
            delay:0.0,
            usingSpringWithDamping:0.40,
            initialSpringVelocity:0.2,
            options:UIViewAnimationOptions.CurveEaseOut,
            animations: {
              self.imageView.transform = CGAffineTransformInvert(expandTransform)
            }, completion:nil)
      })
    }
  }
}

var imageView:UIImageView

如果imageView作为子视图正确添加到视图中,则在selected = falseselected = true之间切换应该会使用弹性动画交换图像。SNStockCellSelectionAccessoryViewImage仅根据当前选择状态返回不同的图像,请参见下文:

private let SNStockCellSelectionAccessoryViewPlusIconSelected:UIImage = UIImage(named:"PlusIconSelected")!
private let SNStockCellSelectionAccessoryViewPlusIcon:UIImage = UIImage(named:"PlusIcon")!

private func SNStockCellSelectionAccessoryViewImage(selected:Bool) -> UIImage {
  return selected ? SNStockCellSelectionAccessoryViewPlusIconSelected : SNStockCellSelectionAccessoryViewPlusIcon
}

下面的 GIF 示例略有减速,实际动画运行更快:

                                        UIImageView 弹跳动画 Gif


29

Swift 5 扩展:

extension UIImageView{
    func setImage(_ image: UIImage?, animated: Bool = true) {
        let duration = animated ? 0.3 : 0.0
        UIView.transition(with: self, duration: duration, options: .transitionCrossDissolve, animations: {
            self.image = image
        }, completion: nil)
    }
}
 

这应该是这个问题的一个被接受的答案。 - Passionate.C

19

代码:

var fadeAnim:CABasicAnimation = CABasicAnimation(keyPath: "contents");

fadeAnim.fromValue = firstImage;
fadeAnim.toValue   = secondImage;
fadeAnim.duration  = 0.8;         //smoothest value

imageView.layer.addAnimation(fadeAnim, forKey: "contents");

imageView.image = secondImage;

示例:

从代码中的UIImageView示例

有趣的、更冗长的解决方案开关触发):

    let fadeAnim:CABasicAnimation = CABasicAnimation(keyPath: "contents");

    switch imageView.image {
        case firstImage?:
            fadeAnim.fromValue = firstImage;
            fadeAnim.toValue   = secondImage;
            imageView.image    = secondImage;
        default:
            fadeAnim.fromValue = secondImage;
            fadeAnim.toValue   = firstImage;
            imageView.image    = firstImage;
    }

    fadeAnim.duration = 0.8;

    imageView.layer.addAnimation(fadeAnim, forKey: "contents");

12

试试这个:

_imageView.image = image;
[_imageView.layer addAnimation:[CATransition animation] forKey:kCATransition];

Swift: _imageView.layer.addAnimation(CATransition(), forKey: kCATransition) - Eugene Braginets

9

使用Swift 3

extension UIImageView{   
 var imageWithFade:UIImage?{
        get{
            return self.image
        }
        set{
            UIView.transition(with: self,
                              duration: 0.5, options: .transitionCrossDissolve, animations: {
                                self.image = newValue
            }, completion: nil)
        }
    }
}

用法:

myImageView.imageWithFade = myImage

7
为什么不试试这个呢。
NSArray *animationFrames = [NSArray arrayWithObjects:
  [UIImage imageWithName:@"image1.png"],
  [UIImage imageWithName:@"image2.png"], 
  nil];

UIImageView *animatedImageView = [[UIImageView alloc] init];
animatedImageView.animationImages = animationsFrame;
[animatedImageView setAnimationRepeatCount:1];
[animatedImageView startAnimating];

一种快速的版本:

let animationsFrames = [UIImage(named: "image1.png"), UIImage(named: "image2.png")]
let animatedImageView = UIImageView()
animatedImageView.animationImages = animationsFrames
animatedImageView.animationRepeatCount = 1
animatedImageView.startAnimating()

这将UIImageView设置为永远重复切换...然而作者只是在寻求一种变化 :( - J-Dizzle
setAnimationRepeatCount - William Hu
SetAnimationRepeatCount 不起作用。它永远不会停止。 - Yucel Bayram

7

Swift 4 这太棒了!

  self.imgViewPreview.transform = CGAffineTransform(scaleX: 0, y: 0)
          UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
              self.imgViewPreview.image = newImage
              self.imgViewPreview.transform = .identity
          }, completion: nil)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接