Fade/dissolve when changing UIImageView's image
Rather than creating tw开发者_JAVA百科o UIImageViews
, it seems logical to simply change the image
of one view. If I do that, is there anyway of having a fade/cross dissolve between the two images rather than an instant switch?
It can be much simpler using the new block-based, UIKit animation
methods.
Suppose the following code is in the view controller, and the UIImageView you want to cross-dissolve is a subview of self.view addressable via the property self.imageView
Then all you need is:
UIImage * toImage = [UIImage imageNamed:@"myname.png"];
[UIView transitionWithView:self.imageView
duration:5.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.imageView.image = toImage;
} completion:nil]
Done.
And to do it in Swift, it's like so:
let toImage = UIImage(named:"myname.png")
UIView.transitionWithView(self.imageView,
duration:5,
options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { self.imageView.image = toImage }, completion: nil)
Swift 3, 4 & 5
let toImage = UIImage(named:"myname.png")
UIView.transition(with: self.imageView,
duration: 0.3,
options: .transitionCrossDissolve,
animations: { self.imageView.image = toImage },
completion: nil)
Edit: there is a better solution from @algal below.
Another way to do this is by using predefined CAAnimation transitions:
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;
See the View Transitions example project from Apple: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411
For Swift 3.0.1 :
UIView.transition(with: self.imageView,
duration:0.5,
options: .transitionCrossDissolve,
animations: { self.imageView.image = newImage },
completion: nil)
Reference: https://gist.github.com/licvido/bc22343cacfa3a8ccf88
Yes what you say is absolutely correct and thats the way to do it. I wrote this method & always use this to Fade in my image. I deal with CALayer
for this. You need to import Core Animation for this.
+ (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;
}
You could do the opposite for Fade out an image. After it fades out. You just remove it from superview (which is UIImageView
). [imageView removeFromSuperview]
.
You could also package the fade-in feature in a subclass, so that you can then use it as a common UIImageView, as in the following example:
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
A possible implementation follows.
Note: the way you actually implement the fade-in in the setImage:
function can change, and could be one of the other excellent examples described in the other answers to this question — creating an additional on-the-fly UIImageView
as I'm doing here might be an unacceptable overhead in your specific situation.
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];
}];
}
}
The above code relies on a few assumptions (including ARC being enabled in your XCode project), is only intended as a proof of concept, and in the interest of clarity and focus, it stays relevant by omitting important unrelated code. Please don't just copy-paste it blindly.
I needed the transition to repeat indefinitely. It took a LOT of trial and error for this one but I finally got the end-result I was looking for. These are code snippets for adding image animation to a UIImageView in a UITableViewCell.
Here is the relevant code:
@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;
}
After playing around with UIView.transition()
and getting problems with .transitionCrossDissolve
option (I was trying to animate images changing inside one UIImageView and transition occurred instantly without animation) I found out that you just need to add one more option which is letting you animate properties changing inside the view (Swift 4.2):
UIView.transition(with: self.imageView,
duration: 1,
options: [.allowAnimatedContent, .transitionCrossDissolve],
animations: { self.imageView.image = newImage },
completion: nil)
In addition: if your have any subviews on your imageView, it will be redrawn as well and it could prevent animation. For example, I had subview with blur on my imageView and in that case animation doesn't work. So I just changed my view hierarchy and move blur to its own view and put it over imageView.
This is I think the shortest way of doing it. Create a UIView animation and commit it on your imageView.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[myImageView setAlpha:0.0];
[UIView commitAnimations];
By using the highlightedImage
property this can be made a bit more simple. Here's an example in Swift 3. First set both normal and highlighted image:
let imageView = UIImageView(image: UIImage(named: "image"), highlightedImage: UIImage(named: "highlightedImage"))
And when you want to change between those animated:
UIView.transition(with: imageView, duration: 0.3, options: .transitionCrossDissolve, animations: { self.imageView.isHighlighted = !self.imageView.isHighlighted}, completion: .none)
If you're using SDWebImage
, you're in luck. SDWebImage ver 4.3.0 onwards has inbuilt fade
& fade(duration:)
methods. DOC
imageView.sd_imageTransition = .fade //.fade(duration: 0.7)
let url = URL(string: "https://foo/bar.jpg")
imageView.sd_setImage(with: url)
精彩评论