在UIImageView中在两个图像之间转换的最佳方式

时间:2022-03-10 20:32:01

I have implemented a pretty simple picture viewer that will allow the user to browse through a collection of images. They are loaded from the Internet, and displayed on the device through a UIImageView object. Something like this:

我实现了一个非常简单的图片查看器,它允许用户浏览一组图片。它们从Internet上加载,并通过UIImageView对象显示在设备上。是这样的:

UIImage *image = [[UIImage alloc] initWithData:imageData];
[img setImage:image];

imageData is an instance of NSData that I use to load the contents of the image from an URL, and img is the UIImageView instance.

imageData是NSData的一个实例,我使用它从URL加载图像的内容,img是UIImageView实例。

It all works well, but the new image replaces the one being displayed before without any transitions, and I was wondering if there is an easy way to do a good animation transition to improve the user experience.

这一切都运行得很好,但是新的图像替换了之前显示的没有任何转换的图像,我想知道是否有一种简单的方法来进行良好的动画转换,以改善用户体验。

Any idea how to do this? Code samples would be very appreciated.

你知道怎么做吗?代码示例将非常感谢。

7 个解决方案

#1


13  

I was just going through your post and had exactly the same requirement. The problem with all above solutions is, you will have to incorporate the logic of transition into your controller. In the sense the approach is not modular. Instead I wrote this subclass of UIImageView:

我刚刚看了你的帖子,有同样的要求。以上所有解决方案的问题是,您必须将转换的逻辑合并到控制器中。从某种意义上说,这种方法不是模块化的。我写了UIImageView的子类

TransitionImageView.h file:

TransitionImageView。h文件:

#import <UIKit/UIKit.h>


@interface TransitionImageView : UIImageView 
{
    UIImageView *mOriginalImageViewContainerView;
    UIImageView *mIntermediateTransitionView;
}
@property (nonatomic, retain) UIImageView *originalImageViewContainerView;
@property (nonatomic, retain) UIImageView *intermediateTransitionView;

#pragma mark -
#pragma mark Animation methods
-(void)setImage:(UIImage *)inNewImage withTransitionAnimation:(BOOL)inAnimation;

@end

TransitionImageView.m file:

TransitionImageView。m文件:

#import "TransitionImageView.h"

#define TRANSITION_DURATION 1.0

@implementation TransitionImageView
@synthesize intermediateTransitionView = mIntermediateTransitionView;
@synthesize originalImageViewContainerView = mOriginalImageViewContainerView;

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        // Initialization code
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

- (void)dealloc 
{
    [self setOriginalImageViewContainerView:nil];
    [self setIntermediateTransitionView:nil];
    [super dealloc];
}

#pragma mark -
#pragma mark Animation methods
-(void)setImage:(UIImage *)inNewImage withTransitionAnimation:(BOOL)inAnimation
{
    if (!inAnimation)
    {
        [self setImage:inNewImage];
    }
    else
    {
        // Create a transparent imageView which will display the transition image.
        CGRect rectForNewView = [self frame];
        rectForNewView.origin = CGPointZero;
        UIImageView *intermediateView = [[UIImageView alloc] initWithFrame:rectForNewView];
        [intermediateView setBackgroundColor:[UIColor clearColor]];
        [intermediateView setContentMode:[self contentMode]];
        [intermediateView setClipsToBounds:[self clipsToBounds]];
        [intermediateView setImage:inNewImage];

        // Create the image view which will contain original imageView's contents:
        UIImageView *originalView = [[UIImageView alloc] initWithFrame:rectForNewView];
        [originalView setBackgroundColor:[UIColor clearColor]];
        [originalView setContentMode:[self contentMode]];
        [originalView setClipsToBounds:[self clipsToBounds]];
        [originalView setImage:[self image]];

        // Remove image from the main imageView and add the originalView as subView to mainView:
        [self setImage:nil];
        [self addSubview:originalView];

        // Add the transparent imageView as subview whose dimensions are same as the view which holds it.
        [self addSubview:intermediateView];

        // Set alpha value to 0 initially:
        [intermediateView setAlpha:0.0];
        [originalView setAlpha:1.0];
        [self setIntermediateTransitionView:intermediateView];
        [self setOriginalImageViewContainerView:originalView];
        [intermediateView release];
        [originalView release];

        // Begin animations:
        [UIView beginAnimations:@"ImageViewTransitions" context:nil];
        [UIView setAnimationDuration:(double)TRANSITION_DURATION];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
        [[self intermediateTransitionView] setAlpha:1.0];
        [[self originalImageViewContainerView] setAlpha:0.0];
        [UIView commitAnimations];
    }
}

-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
    // Reset the alpha of the main imageView
    [self setAlpha:1.0];

    // Set the image to the main imageView:
    [self setImage:[[self intermediateTransitionView] image]];

    [[self intermediateTransitionView] removeFromSuperview];
    [self setIntermediateTransitionView:nil];

    [[self originalImageViewContainerView] removeFromSuperview];
    [self setOriginalImageViewContainerView:nil];
}

@end

You can even override the -setImage method of UIImageView and call my -setImage:withTransitionAnimation: method. If it is done like this make sure that you call [super setImage:] instead of [self setImage:] in the method -setImage:withTransitionAnimation: so that it wont end up in infinite recursive call!

您甚至可以覆盖UIImageView的-setImage方法,并调用我的-setImage:withTransitionAnimation: method。如果这样做,请确保在方法中调用[super setImage:]而不是[self setImage:] -setImage:withTransitionAnimation:这样它就不会以无限递归调用结束!

-Raj

拉吉

#2


8  

[UIView 
    animateWithDuration:0.2 
    delay:0 
    options:UIViewAnimationCurveEaseOut
    animations:^{
        self.view0.alpha = 0;
        self.view1.alpha = 1;
    }
    completion:^(BOOL finished){
        view0.hidden = YES;
    }
];

#3


3  

The trick is you make two UIImageView instances. You swap them in between calls to UIView +beginAnimations and +commitAnimations.

诀窍是创建两个UIImageView实例。你在调用UIView + beginanimation和+ commitanimation之间交换它们。

#4


3  

Nothing different from what's been explained but in code, these are the available transitions:

没有什么不同的解释,但在代码中,这些是可用的转换:

typedef enum {
        UIViewAnimationTransitionNone,
        UIViewAnimationTransitionFlipFromLeft,
        UIViewAnimationTransitionFlipFromRight,
        UIViewAnimationTransitionCurlUp,
        UIViewAnimationTransitionCurlDown,
    } UIViewAnimationTransition;

Code (Put this in a callback like touchesEnded):

代码(将其放入类似touchesEnded的回调中):

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];

[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:[self superview] cache:YES];

// -- These don't work on the simulator and the curl up will turn into a fade -- //
//[UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:[self superview] cache:YES];
//[UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:[self superview] cache:YES];

[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];

// Below assumes you have two subviews that you're trying to transition between
[[self superview] exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
[UIView commitAnimations];

#5


3  

Please check the answer. I think you are looking for this:

请检查答案。我想你是在找这个:

imgvw.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"Your Image name as string"]]];
CATransition *transition = [CATransition animation];
transition.duration = 1.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
[imgvw.layer addAnimation:transition forKey:nil];

#6


1  

There are several ways to do it and I agree with Ben View Transitions is a great example. If you are looking for simple full screen transitions I would just consider starting a new utility application and look at toggleView method in the RootViewController.m. Try switching out the UIViewAnimationTransitionFlipFromLeft and UIViewAnimationTransitionFlipFromRight to UIViewAnimationTransitionCurlUp and UIViewAnimationTransitionCurlDown for a really nice transition effect (this only works on the device).

有几种方法可以做到这一点,我同意Ben View transition就是一个很好的例子。如果您正在寻找简单的全屏转换,我将考虑启动一个新的实用程序应用程序,并在RootViewController.m中查看toggleView方法。试着把uiviewanimationtransitionfromleft和UIViewAnimationTransitionFlipFromRight切换到UIViewAnimationTransitionCurlUp和UIViewAnimationTransitionCurlDown切换到一个很好的过渡效果(这只对设备有效)。

#7


0  

here is what i have done: a fading. i put another UIImageView with the same UIImage and dimension called tmp. i replace the UIImage of the base UIImageView. Then i put the right image on the base (still covered by tmp).

这就是我所做的:衰落。我放了另一个UIImageView带有相同的UIImage和维度叫做tmp。我替换了基本UIImageView的UIImage。然后我把正确的图像放在基地(仍然被tmp覆盖)。

Next step is - to set alpha of tmp to zero, - to stretch the base UIImageView to right ratio of the new UIImage based on the height of the base.

下一步是——将tmp的alpha设置为零,根据基础的高度将基础UIImageView拉伸到新的UIImage的正确比例。

Here is the code:

这是代码:

    UIImage *img = [params objectAtIndex:0]; // the right image
UIImageView *view = [params objectAtIndex:1]; // the base

UIImageView *tmp = [[UIImageView alloc] initWithImage:view.image]; // the one which will use to fade
tmp.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
[view addSubview:tmp];

view.image = img;
float r = img.size.width / img.size.height;
float h = view.frame.size.height;
float w = h * r;
float x = view.center.x - w/2;
float y = view.frame.origin.y;

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];

tmp.alpha = 0;
view.frame = CGRectMake(x, y, w, h);

[UIView commitAnimations];

[tmp performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.5];
[tmp performSelector:@selector(release) withObject:nil afterDelay:2];

#1


13  

I was just going through your post and had exactly the same requirement. The problem with all above solutions is, you will have to incorporate the logic of transition into your controller. In the sense the approach is not modular. Instead I wrote this subclass of UIImageView:

我刚刚看了你的帖子,有同样的要求。以上所有解决方案的问题是,您必须将转换的逻辑合并到控制器中。从某种意义上说,这种方法不是模块化的。我写了UIImageView的子类

TransitionImageView.h file:

TransitionImageView。h文件:

#import <UIKit/UIKit.h>


@interface TransitionImageView : UIImageView 
{
    UIImageView *mOriginalImageViewContainerView;
    UIImageView *mIntermediateTransitionView;
}
@property (nonatomic, retain) UIImageView *originalImageViewContainerView;
@property (nonatomic, retain) UIImageView *intermediateTransitionView;

#pragma mark -
#pragma mark Animation methods
-(void)setImage:(UIImage *)inNewImage withTransitionAnimation:(BOOL)inAnimation;

@end

TransitionImageView.m file:

TransitionImageView。m文件:

#import "TransitionImageView.h"

#define TRANSITION_DURATION 1.0

@implementation TransitionImageView
@synthesize intermediateTransitionView = mIntermediateTransitionView;
@synthesize originalImageViewContainerView = mOriginalImageViewContainerView;

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        // Initialization code
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

- (void)dealloc 
{
    [self setOriginalImageViewContainerView:nil];
    [self setIntermediateTransitionView:nil];
    [super dealloc];
}

#pragma mark -
#pragma mark Animation methods
-(void)setImage:(UIImage *)inNewImage withTransitionAnimation:(BOOL)inAnimation
{
    if (!inAnimation)
    {
        [self setImage:inNewImage];
    }
    else
    {
        // Create a transparent imageView which will display the transition image.
        CGRect rectForNewView = [self frame];
        rectForNewView.origin = CGPointZero;
        UIImageView *intermediateView = [[UIImageView alloc] initWithFrame:rectForNewView];
        [intermediateView setBackgroundColor:[UIColor clearColor]];
        [intermediateView setContentMode:[self contentMode]];
        [intermediateView setClipsToBounds:[self clipsToBounds]];
        [intermediateView setImage:inNewImage];

        // Create the image view which will contain original imageView's contents:
        UIImageView *originalView = [[UIImageView alloc] initWithFrame:rectForNewView];
        [originalView setBackgroundColor:[UIColor clearColor]];
        [originalView setContentMode:[self contentMode]];
        [originalView setClipsToBounds:[self clipsToBounds]];
        [originalView setImage:[self image]];

        // Remove image from the main imageView and add the originalView as subView to mainView:
        [self setImage:nil];
        [self addSubview:originalView];

        // Add the transparent imageView as subview whose dimensions are same as the view which holds it.
        [self addSubview:intermediateView];

        // Set alpha value to 0 initially:
        [intermediateView setAlpha:0.0];
        [originalView setAlpha:1.0];
        [self setIntermediateTransitionView:intermediateView];
        [self setOriginalImageViewContainerView:originalView];
        [intermediateView release];
        [originalView release];

        // Begin animations:
        [UIView beginAnimations:@"ImageViewTransitions" context:nil];
        [UIView setAnimationDuration:(double)TRANSITION_DURATION];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
        [[self intermediateTransitionView] setAlpha:1.0];
        [[self originalImageViewContainerView] setAlpha:0.0];
        [UIView commitAnimations];
    }
}

-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
    // Reset the alpha of the main imageView
    [self setAlpha:1.0];

    // Set the image to the main imageView:
    [self setImage:[[self intermediateTransitionView] image]];

    [[self intermediateTransitionView] removeFromSuperview];
    [self setIntermediateTransitionView:nil];

    [[self originalImageViewContainerView] removeFromSuperview];
    [self setOriginalImageViewContainerView:nil];
}

@end

You can even override the -setImage method of UIImageView and call my -setImage:withTransitionAnimation: method. If it is done like this make sure that you call [super setImage:] instead of [self setImage:] in the method -setImage:withTransitionAnimation: so that it wont end up in infinite recursive call!

您甚至可以覆盖UIImageView的-setImage方法,并调用我的-setImage:withTransitionAnimation: method。如果这样做,请确保在方法中调用[super setImage:]而不是[self setImage:] -setImage:withTransitionAnimation:这样它就不会以无限递归调用结束!

-Raj

拉吉

#2


8  

[UIView 
    animateWithDuration:0.2 
    delay:0 
    options:UIViewAnimationCurveEaseOut
    animations:^{
        self.view0.alpha = 0;
        self.view1.alpha = 1;
    }
    completion:^(BOOL finished){
        view0.hidden = YES;
    }
];

#3


3  

The trick is you make two UIImageView instances. You swap them in between calls to UIView +beginAnimations and +commitAnimations.

诀窍是创建两个UIImageView实例。你在调用UIView + beginanimation和+ commitanimation之间交换它们。

#4


3  

Nothing different from what's been explained but in code, these are the available transitions:

没有什么不同的解释,但在代码中,这些是可用的转换:

typedef enum {
        UIViewAnimationTransitionNone,
        UIViewAnimationTransitionFlipFromLeft,
        UIViewAnimationTransitionFlipFromRight,
        UIViewAnimationTransitionCurlUp,
        UIViewAnimationTransitionCurlDown,
    } UIViewAnimationTransition;

Code (Put this in a callback like touchesEnded):

代码(将其放入类似touchesEnded的回调中):

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];

[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:[self superview] cache:YES];

// -- These don't work on the simulator and the curl up will turn into a fade -- //
//[UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:[self superview] cache:YES];
//[UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:[self superview] cache:YES];

[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];

// Below assumes you have two subviews that you're trying to transition between
[[self superview] exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
[UIView commitAnimations];

#5


3  

Please check the answer. I think you are looking for this:

请检查答案。我想你是在找这个:

imgvw.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"Your Image name as string"]]];
CATransition *transition = [CATransition animation];
transition.duration = 1.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
[imgvw.layer addAnimation:transition forKey:nil];

#6


1  

There are several ways to do it and I agree with Ben View Transitions is a great example. If you are looking for simple full screen transitions I would just consider starting a new utility application and look at toggleView method in the RootViewController.m. Try switching out the UIViewAnimationTransitionFlipFromLeft and UIViewAnimationTransitionFlipFromRight to UIViewAnimationTransitionCurlUp and UIViewAnimationTransitionCurlDown for a really nice transition effect (this only works on the device).

有几种方法可以做到这一点,我同意Ben View transition就是一个很好的例子。如果您正在寻找简单的全屏转换,我将考虑启动一个新的实用程序应用程序,并在RootViewController.m中查看toggleView方法。试着把uiviewanimationtransitionfromleft和UIViewAnimationTransitionFlipFromRight切换到UIViewAnimationTransitionCurlUp和UIViewAnimationTransitionCurlDown切换到一个很好的过渡效果(这只对设备有效)。

#7


0  

here is what i have done: a fading. i put another UIImageView with the same UIImage and dimension called tmp. i replace the UIImage of the base UIImageView. Then i put the right image on the base (still covered by tmp).

这就是我所做的:衰落。我放了另一个UIImageView带有相同的UIImage和维度叫做tmp。我替换了基本UIImageView的UIImage。然后我把正确的图像放在基地(仍然被tmp覆盖)。

Next step is - to set alpha of tmp to zero, - to stretch the base UIImageView to right ratio of the new UIImage based on the height of the base.

下一步是——将tmp的alpha设置为零,根据基础的高度将基础UIImageView拉伸到新的UIImage的正确比例。

Here is the code:

这是代码:

    UIImage *img = [params objectAtIndex:0]; // the right image
UIImageView *view = [params objectAtIndex:1]; // the base

UIImageView *tmp = [[UIImageView alloc] initWithImage:view.image]; // the one which will use to fade
tmp.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
[view addSubview:tmp];

view.image = img;
float r = img.size.width / img.size.height;
float h = view.frame.size.height;
float w = h * r;
float x = view.center.x - w/2;
float y = view.frame.origin.y;

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];

tmp.alpha = 0;
view.frame = CGRectMake(x, y, w, h);

[UIView commitAnimations];

[tmp performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.5];
[tmp performSelector:@selector(release) withObject:nil afterDelay:2];