Desbotar / dissolver ao alterar a imagem do UIImageView

160

Em vez de criar duas UIImageViews, parece lógico simplesmente alterar a imagevisão de uma. Se eu fizer isso, existe uma forma de dissolver / desvanecer / dissolver entre as duas imagens em vez de uma troca instantânea?

Josh Kahane
fonte
1
@ kumar-kl Sua edição removeu uma tag importante. Por favor, não faça isso.
Eric Aya
1
@KumarKL Mas aqui "objetivo-c" não é uma marca de baixa prioridade! Não remova-o apenas para adicionar "veloz", isso não faz sentido, é na verdade um vandalismo limítrofe ...
Eric Aya
1
@ Eric, Ok, não vou repetir o mesmo. Obrigado pela preocupação.
Kumar KL

Respostas:

45

Edit: existe uma solução melhor em @algal abaixo .

Outra maneira de fazer isso é usar transições predefinidas do 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;

Consulte o projeto de exemplo View Transitions da Apple: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411

Mirkules
fonte
1
Perfeito! Eu adiciono parte desse código à categoria UIImageView (WebCache) da SDWebImage.
Autobots
@OutMan, pode ser melhor usar o método mais recente abaixo, pelo usuário577888.
Mirkules
Também estou definindo minha imagem após um retorno de chamada SDWebImages dessa maneira, mas por algum motivo em meu CollectionViewCell, a imagem da primeira célula é inicialmente a da última célula visível, se todas as minhas células forem atualizadas. Eu acho que é algum tipo de cache da apresentaçãoLayer ou algo assim?!? Alguma idéia do que isso possa ser?
25414 Georg Georg
Voto positivo para a edição "há uma solução melhor do @algal abaixo."
Rob
581

Pode ser muito mais simples usar os novos UIKit animationmétodos baseados em blocos .

Suponha que o código a seguir esteja no controlador de exibição, e o UIImageView que você deseja dissolver é uma subvisão de self.view endereçável por meio da propriedade self.imageViewEntão, tudo o que você precisa é:

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

Feito.

E para fazer isso no Swift, é assim:

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

     let toImage = UIImage(named:"myname.png")
     UIView.transition(with: self.imageView,
                       duration: 0.3,
                       options: .transitionCrossDissolve,
                       animations: {
                           self.imageView.image = toImage
                       },
                       completion: nil)
algal
fonte
3
As outras respostas estão corretas, mas desatualizadas (e / ou excessivamente detalhadas).
21412 Steven Kramer
3
Não é necessário fazer a transição na super visão. Consegui fazer isso funcionar especificando o próprio UIImageView como destino.
Desmond
7
@ user577888 apenas uma coisa pequena. Você deve passar nulo para o bloco de conclusão vazio. Os blocos não são ponteiros de função (como indicado pelo ^) e funcionam como um objeto objetivo-c. Se você passar NULL, o compilador interpretará isso como 0 ou (vazio *) 0. Se, por algum motivo, o código subjacente do transiçãoWithView: ... acidentalmente enviar uma mensagem ao bloco de conclusão (por exemplo, [cópia de conclusão]) sem verificar sua validade, isso levará a um erro. Portanto, você sempre deve usar o nulo do objetivo-c ao definir um bloco como vazio.
Sr. T
3
@ Mr.T NULL e nil são idênticos em valor. Ambos são definidos como 0 e nem é provável que mude. Portanto, é seguro usar NULL em qualquer lugar que você usaria ou poderia usar nulo.
Ashevin
1
@ Mr.T O que quero dizer é que o uso de um sobre o outro nunca causará uma falha, porque no nível do código compilado eles são idênticos e, por razões práticas, sempre será. Suprimir avisos do compilador é uma boa idéia, em geral, mas dizer às pessoas que é um erro usar NULL em vez de nulo está simplesmente errado.
Ashevin
6

Sim, o que você diz é absolutamente correto e é esse o caminho. Eu escrevi esse método e sempre o uso para desaparecer na minha imagem. Eu lido com CALayerisso. Você precisa importar o Core Animation para isso.

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

Você pode fazer o oposto para desvanecer uma imagem. Depois que desaparece. Você acabou de removê-lo da superview (que é UIImageView). [imageView removeFromSuperview].

Srikar Appalaraju
fonte
Eu adiciono esse código após definir uma imagem no UIImageView, parece que há um piscar de olhos ao iniciar a animação.
Autobots
4

Você também pode empacotar o recurso de desvanecimento em uma subclasse, para poder usá-lo como um UIImageView comum, como no exemplo a seguir:

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

Uma possível implementação a seguir.

Nota: a maneira como você realmente implementa o desvanecimento na setImage:função pode mudar e pode ser um dos outros excelentes exemplos descritos nas outras respostas a esta pergunta - criando um adicional imediato, UIImageViewcomo eu estou fazendo aqui. ser uma sobrecarga inaceitável na sua situação 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];
        }];
    }
}

O código acima baseia-se em algumas suposições (incluindo a ativação do ARC no seu projeto XCode), destina-se apenas como uma prova de conceito e, por questões de clareza e foco, permanece relevante ao omitir códigos não relacionados importantes. Por favor, não copie e cole cegamente.

magma
fonte
3

Eu precisava que a transição se repetisse indefinidamente. Foram necessárias muitas tentativas e erros para este, mas finalmente consegui o resultado final que estava procurando. Esses são trechos de código para adicionar animação de imagem a um UIImageView em um UITableViewCell.

Aqui está o 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
fonte
Oi, eu queria saber se você poderia adicionar mais algumas informações no seu [self setVarietyImageView: cell.imageView]; método como posso obter a última célula para animar ou todos para animar, mas muito rápido, Graças
GAV
Olá Gav, "variedadeImageView" é uma referência fraca no controlador de exibição, para que eu possa atribuir ao cell.imageView e referenciá-lo posteriormente no método 'animateImages'. @property (não atômica, fraca) UIImageView * variedadeImageView; Você pode fazer as imagens girarem mais rapidamente, definindo uma "duração" mais curta em "transitWithView". Espero que seja isso que você está perguntando.
Christopher
Olá Christopher, obrigado pela resposta. Fiquei me perguntando sobre o "setVarietyImageView:" Suponho que seja algum tipo de método nulo que permita a reutilização, pois só consegui animar a última célula. Novamente Obrigado por responder, tudo de bom Gav
gav
Este método é gerado usando a declaração "propriedade" padrão. Por exemplo, @property (não atômico, fraco) UIImageView * variedadeImageView. É uma referência "fraca", pois se refere a outra variável local (o imageView da célula da tabela). Em vez disso, eu poderia ter feito uma referência à célula da tabela e acessar o cell.imageView no método animateImages. Eu espero que isso ajude.
Christopher
2

Depois de brincar UIView.transition()e ter problemas com a .transitionCrossDissolveopção (eu estava tentando animar as imagens que mudam dentro de um UIImageView e a transição ocorreu instantaneamente sem animação), descobri que você só precisa adicionar mais uma opção, que permite animar as propriedades que mudam dentro da exibição ( Swift 4.2 ):

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

Além disso: se você tiver subviews no imageView, ele também será redesenhado e poderá impedir a animação. Por exemplo, eu tive uma subvisão com desfoque no meu imageView e, nesse caso, a animação não funciona. Então, mudei minha hierarquia de vistas e movi o desfoque para sua própria vista e a coloquei sobre o imageView.

Pavel
fonte
0

Esta é a maneira mais curta de fazê-lo. Crie uma animação UIView e confirme no seu imageView.

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

Usando a highlightedImagepropriedade, isso pode ser um pouco mais simples. Aqui está um exemplo no Swift 3. Primeiro, defina a imagem normal e a destacada:

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

E quando você quiser alternar entre os animados:

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