Desvanecerse / disolverse al cambiar la imagen de UIImageView

160

En lugar de crear dos UIImageViews, parece lógico simplemente cambiar el imagede una vista. Si hago eso, ¿hay alguna forma de disolver el fundido / cruce entre las dos imágenes en lugar de un cambio instantáneo?

Josh Kahane
fuente
1
@ kumar-kl Su edición eliminó una etiqueta importante. Por favor no hagas eso.
Eric Aya
1
@KumarKL ¡Pero aquí "Objective-C" no es una etiqueta de baja prioridad! No lo elimine solo para agregar "rápido", esto no tiene sentido, en realidad es vandalismo límite ...
Eric Aya
1
@EricD, Ok, no repetiré lo mismo. Gracias por tu preocupación.
Kumar KL

Respuestas:

45

Editar: hay una mejor solución de @algal a continuación .

Otra forma de hacerlo es mediante el uso de transiciones CAAnimation predefinidas:

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;

Consulte el proyecto de ejemplo Ver transiciones de Apple: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411

Mirkules
fuente
1
¡Perfecto! Agrego parte de este código en la categoría UIImageView (WebCache) de SDWebImage.
Autobots
@OutMan, puede que sea mejor usar el método más reciente a continuación por user577888.
Mirkules
También estoy configurando mi imagen después de una devolución de llamada SDWebImages de esa manera, pero por alguna razón en mi CollectionViewCell, la imagen de la primera celda es inicialmente la de la última celda visible si todas mis celdas se actualizan. ¿Supongo que es algún tipo de almacenamiento en caché de la presentación en capas o algo así? ¿Alguna idea de lo que podría ser?
Georg
Voto a favor de la edición "hay una mejor solución de @algal a continuación".
Rob
581

Puede ser mucho más simple usar los nuevos UIKit animationmétodos basados ​​en bloques .

Suponga que el siguiente código está en el controlador de vista, y el UIImageView que desea disolver en forma cruzada es una subvista de self.view direccionable a través de la propiedad. self.imageViewEntonces todo lo que necesita es:

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

Hecho.

Y para hacerlo en Swift, es así:

    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 y 5

     let toImage = UIImage(named:"myname.png")
     UIView.transition(with: self.imageView,
                       duration: 0.3,
                       options: .transitionCrossDissolve,
                       animations: {
                           self.imageView.image = toImage
                       },
                       completion: nil)
alga
fuente
3
Las otras respuestas son correctas pero desactualizadas (y / o demasiado detalladas).
Steven Kramer
3
No es necesario hacer la transición en la súper vista. Logré hacer que esto funcione especificando el propio UIImageView como el objetivo.
Desmond el
77
@ user577888 solo una pequeña cosa. Debe pasar nulo al bloque de finalización vacío. Los bloques no son punteros de función (como se denota con ^) y funcionan como un objeto object-c. Si pasa NULL, el compilador lo interpretará como 0 o (void *) 0. Si, por alguna razón, el código subyacente para transitionWithView: ... envía accidentalmente un mensaje al bloque de finalización (por ejemplo, [copia de finalización]) sin verificar su validez, esto generará un error. Por lo tanto, siempre debe usar el objetivo n-c cuando configura un bloque para que esté vacío.
Sr. T
3
@ Mr.T NULL y nil son idénticos en valor. Ambos están definidos en 0 y ninguno de ellos es probable que cambie. Por lo tanto, es seguro usar NULL en cualquier lugar donde quiera o pueda usar nulo.
ashevin
1
@ Mr.T Mi punto es que usar uno sobre el otro nunca causará un colapso porque en el nivel del código compilado son idénticos, y por razones prácticas, siempre lo serán. Suprimir las advertencias del compilador es una buena idea, en general, pero decirle a la gente que es un error usar NULL en lugar de nil es simplemente incorrecto.
ashevin
6

Sí, lo que dices es absolutamente correcto y esa es la forma de hacerlo. Escribí este método y siempre lo uso para desvanecerse en mi imagen. Trato con CALayeresto. Necesita importar Core Animation para esto.

+ (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;
}

Podrías hacer lo contrario para atenuar una imagen. Después de que se desvanece. Simplemente lo eliminas de la supervista (que es UIImageView). [imageView removeFromSuperview].

Srikar Appalaraju
fuente
Agregué este código después de configurar una imagen en UIImageView, parece que hay un parpadeo al comenzar la animación.
Autobots
4

También puede empaquetar la función de fundido de entrada en una subclase, para que luego pueda usarla como un UIImageView común, como en el siguiente ejemplo:

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

Sigue una posible implementación.

Nota: la forma en que realmente implementa el fundido de entrada en la setImage:función puede cambiar, y podría ser uno de los otros excelentes ejemplos descritos en las otras respuestas a esta pregunta; crear un sobre la marcha adicional UIImageViewcomo lo estoy haciendo aquí podría Ser una sobrecarga inaceptable en su situación específica .

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];
        }];
    }
}

El código anterior se basa en algunas suposiciones (incluida la habilitación de ARC en su proyecto XCode), solo pretende ser una prueba de concepto, y en aras de la claridad y el enfoque, sigue siendo relevante omitiendo código importante no relacionado. Por favor, no solo copie y pegue a ciegas.

magma
fuente
3

Necesitaba que la transición se repitiera indefinidamente. Tomó MUCHA prueba y error para este, pero finalmente obtuve el resultado final que estaba buscando. Estos son fragmentos de código para agregar animación de imágenes a un UIImageView en un UITableViewCell.

Aquí está el código relevante:

@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;
}
Christopher
fuente
Hola, me preguntaba si podría agregar más información sobre su [self setVarietyImageView: cell.imageView]; método, ya que puedo hacer que la última celda se anime o todos se animen pero realmente rápido, gracias
gav
Hola Gav, "varietyImageView" es una referencia débil en el controlador de vista, por lo que puedo asignarla a cell.imageView y hacer referencia más adelante en el método 'animateImages'. @property (no atómico, débil) UIImageView * varietyImageView; Puede hacer que las imágenes giren más rápido estableciendo una "duración" más corta en "transitionWithView". Esperemos que esto sea lo que estás preguntando.
Christopher
Hola Christopher, gracias por la respuesta, me preguntaba sobre "setVarietyImageView:" Supongo que es algún tipo de método nulo que permite la reutilización, ya que solo he podido animar la última celda. Gracias de nuevo por responder, todo lo mejor Gav
gav
Este método se genera utilizando la declaración estándar de "propiedad". Por ejemplo, @property (no atómico, débil) UIImageView * varietyImageView. Es una referencia "débil" ya que se refiere a otra variable local (la imageView de la celda de la tabla). En cambio, podría haber hecho una referencia a la celda de la tabla y acceder a cell.imageView en el método animateImages. Espero que esto ayude.
Christopher
2

Después de jugar UIView.transition()y tener problemas con la .transitionCrossDissolveopción (estaba tratando de animar las imágenes que cambian dentro de un UIImageView y la transición se produjo instantáneamente sin animación) descubrí que solo necesita agregar una opción más que le permite animar las propiedades que cambian dentro de la vista ( Swift 4.2 ):

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

Además: si tiene subvistas en su imageView, también se volverá a dibujar y podría evitar la animación. Por ejemplo, tenía una subvista con desenfoque en mi imageView y en ese caso la animación no funciona. Así que simplemente cambié mi jerarquía de vistas y moví el desenfoque a su propia vista y lo puse sobre imageView.

Pavel
fuente
0

Esta es, creo, la forma más corta de hacerlo. Cree una animación UIView y comprométala en su imageView.

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

Al usar la highlightedImagepropiedad, esto se puede hacer un poco más simple. Aquí hay un ejemplo en Swift 3. Primero configure la imagen normal y la resaltada:

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

Y cuando quieras cambiar entre esos animados:

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