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?


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


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)

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].


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.


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

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.


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

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.