更改 UIImageView 图像时淡出/消除

与其创建两个 UIImageViews,不如简单地更改一个视图的 image。如果我这样做,是否有任何方式有褪色/交叉溶解之间的两个图像,而不是一个即时开关?

95607 次浏览

是的,你说的完全正确,这就是做事的方法。我写了这个方法,并且总是使用这个方法在我的图像中淡出。我和 CALayer做生意就是为了这个。您需要为此导入核心动画。

+ (void)fadeInLayer:(CALayer *)l
{
CABasicAnimation *fadeInAnimate   = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimate.duration            = 0.5;
fadeInAnimate.repeatCount         = 1;
fadeInAnimate.autoreverses        = NO;
fadeInAnimate.fromValue           = [NSNumber numberWithFloat:0.0];
fadeInAnimate.toValue             = [NSNumber numberWithFloat:1.0];
fadeInAnimate.removedOnCompletion = YES;
[l addAnimation:fadeInAnimate forKey:@"animateOpacity"];
return;
}

你可以做相反的淡出一个图像。在它消失之后。您只需将其从 superview (即 UIImageView)中删除。[imageView removeFromSuperview].

编辑: 有一个更好的解决方案从 @ algal under

另一种方法是使用预定义的 CAAnimation 转换:

CATransition *transition = [CATransition animation];
transition.duration = 0.25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
transition.delegate = self;
[self.view.layer addAnimation:transition forKey:nil];
view1.hidden = YES;
view2.hidden = NO;

参见来自 Apple 的视图转换示例项目: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411

使用新的基于块的 UIKit animation方法可以简单得多。

假设下面的代码在视图控制器中,你想交叉溶解的 UIImageView 是 self. view 的子视图,可以通过属性 self.imageView进行寻址:

UIImage * toImage = [UIImage imageNamed:@"myname.png"];
[UIView transitionWithView:self.imageView
duration:5.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.imageView.image = toImage;
} completion:nil]

成交。

用斯威夫特的方法,就像这样:

let toImage = UIImage(named:"myname.png")
UIView.transitionWithView(self.imageView,
duration:5,
options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { self.imageView.image = toImage }, completion: nil)

斯威夫特三,四,五

let toImage = UIImage(named:"myname.png")
UIView.transition(with: self.imageView,
duration: 0.3,
options: .transitionCrossDissolve,
animations: { self.imageView.image = toImage },
completion: nil)

您还可以将淡入特性打包到一个子类中,这样您就可以将其用作一个通用的 UIImageView,如下面的示例所示:

IMMFadeImageView *fiv=[[IMMFadeImageView alloc] initWithFrame:CGRectMake(10, 10, 50, 50)];
[self.view addSubview:fiv];
fiv.image=[UIImage imageNamed:@"initialImage.png"];
fiv.image=[UIImage imageNamed:@"fadeinImage.png"];  // fades in

接下来是一个可能的实现。

注意: 在 setImage:函数中实现淡入的方式可能会发生变化,这可能是这个问题的其他答案中描述的另一个很好的例子之一ーー像我在这里所做的那样,创建一个额外的动态 UIImageView,在您的特定情况下,这可能是一个不可接受的开销。

IMMFadeImageView.h :

#import <UIKit/UIKit.h>


@interface IMMFadeImageView : UIImageView
@property (nonatomic,assign) float fadeDuration;
@end

IMMFadeImageView.m :

#import "IMMFadeImageView.h"


@implementation IMMFadeImageView
@synthesize fadeDuration;


- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.fadeDuration=1;
}
return self;
}
-(void)setImage:(UIImage *)newImage{


if(!self.image||self.fadeDuration<=0){
super.image=newImage;
} else {
UIImageView *iv=[[UIImageView alloc] initWithFrame:self.bounds];
iv.contentMode=self.contentMode;
iv.image=super.image;
iv.alpha=1;
[self addSubview:iv];
super.image=newImage;
[UIView animateWithDuration:self.fadeDuration delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
iv.alpha=0;
} completion:^(BOOL finished) {
[iv removeFromSuperview];
}];
}
}

上面的代码依赖于一些假设(包括在您的 XCode 项目中启用 ARC) ,它只是作为概念的证明,为了清晰和集中,它省略了重要的不相关代码以保持相关性。请不要盲目地复制粘贴。

我需要无限期地重复这个过渡。这一次我尝试了很多次,但最终我还是得到了我想要的结果。这些代码片段用于将图像动画添加到 UITableViewCell 中的 UIImageView。

以下是相关代码:

@interface SomeViewController ()
@property(nonatomic, strong) NSMutableArray *imagesArray;
@property(nonatomic, assign) NSInteger varietyImageAnimationIndex;
@property(nonatomic, assign) BOOL varietyImagesAnimated;
@end


@implementation SomeViewController
@synthesize imagesArray;
@synthesize varietyImageAnimationIndex;
@synthesize varietyImagesAnimated;
...


// NOTE: Initialize the array of images in perhaps viewDidLoad method.


-(void)animateImages
{
varietyImageAnimationIndex++;


[UIView transitionWithView:varietyImageView
duration:2.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
varietyImageView.image = [imagesArray objectAtIndex:varietyImageAnimationIndex % [imagesArray count]];
} completion:^(BOOL finished) {
[self animateImages];
}];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
[cell.imageView setImage:[imagesArray objectAtIndex:0]];




[self setVarietyImageView:cell.imageView];


if (! varietyImagesAnimated)
{
varietyImagesAnimated = YES;
[self animateImages];
}


...
return cell;
}

这是我认为做到这一点的最短的方法。创建一个 UIView 动画,并提交它在您的 image 视图。

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[myImageView setAlpha:0.0];
[UIView commitAnimations];

通过使用 highlightedImage属性,这可以变得更加简单一些。以下是 Swift 3中的一个例子。首先设置正常和突出显示的图像:

let imageView = UIImageView(image: UIImage(named: "image"), highlightedImage: UIImage(named: "highlightedImage"))

当你想在这些动画片之间切换时:

UIView.transition(with: imageView, duration: 0.3, options: .transitionCrossDissolve, animations: { self.imageView.isHighlighted = !self.imageView.isHighlighted}, completion: .none)

对于 Swift 3.0.1:

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

参考资料: https://gist.github.com/licvido/bc22343cacfa3a8ccf88

在使用了 UIView.transition().transitionCrossDissolve选项之后(我试图在一个 UIImageView 中动画图像的变化,并且在没有动画的情况下立即发生了转换) ,我发现你只需要再添加一个选项,就可以在视图中动画属性的变化(Swift 4.2) :

UIView.transition(with: self.imageView,
duration: 1,
options: [.allowAnimatedContent, .transitionCrossDissolve],
animations: { self.imageView.image = newImage },
completion: nil)

另外: 如果你有任何子视图在你的 imageView 上,它也将被重新绘制,它可以防止动画。例如,我在 image View 中使用了模糊的子视图,在这种情况下,动画不起作用。因此,我只是改变了我的视图层次结构,并将模糊移动到它自己的视图,并将其放在 imageView 上。

如果你使用的是 SDWebImage,那你就走运了。 SDWebImage 4.3.0版本以后已经内置了 fadefade(duration:)方法

imageView.sd_imageTransition = .fade  //.fade(duration: 0.7)
let url = URL(string: "https://foo/bar.jpg")
imageView.sd_setImage(with: url)