Zanikanie / rozpuszczanie podczas zmiany obrazu UIImageView


160

Zamiast tworzyć dwa UIImageViews, logiczne wydaje się po prostu zmienić imagejeden widok. Jeśli to zrobię, czy w ogóle występuje zanikanie / krzyżowanie się między dwoma obrazami, a nie natychmiastowa zmiana?


1
@ kumar-kl Twoja zmiana usunęła ważny tag. Proszę, nie rób tego.
— Eric Aya,

1
@KumarKL Ale tutaj „objective-c” nie jest tagiem o niskim priorytecie! Nie usuwaj go tylko po to, by dodać „swift”, to nie ma sensu, w rzeczywistości jest to wandalizm z pogranicza ...
— Eric Aya

1
@EricD, Ok, nie powtórzę tego samego. Dzięki za troskę.
— Kumar KL

Odpowiedzi:


45

Edycja: poniżej jest lepsze rozwiązanie z @algal .

Innym sposobem jest użycie wstępnie zdefiniowanych przejść 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;

Zobacz przykładowy projekt View Transitions firmy Apple: https://developer.apple.com/library/content/samplecode/ViewTransitions/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007411


1
Idealny! Dodaję część tego kodu do kategorii UIImageView (WebCache) w SDWebImage.
— Autoboty

@OutMan może być lepiej, korzystając z nowszej metody poniżej przez user577888.
— Mirkules,

W ten sposób ustawiam również mój obraz po wywołaniu zwrotnym SDWebImages, ale z jakiegoś powodu w mojej CollectionViewCell obraz pierwszej komórki jest początkowo obrazem z ostatniej widocznej komórki, jeśli wszystkie moje komórki zostaną zaktualizowane. Myślę, że to jakiś rodzaj buforowania PresentationLayer czy coś takiego?!? Jakieś pomysły, co to może być?
— Georg,

Głosuj za edycją „jest lepsze rozwiązanie od @algal poniżej”.
— Rob

581

Korzystanie z nowych UIKit animationmetod opartych na blokach może być znacznie prostsze .

Załóżmy, że następujący kod znajduje się w kontrolerze widoku, a UIImageView, który chcesz rozwiązać krzyżowo, jest podwidokiem self.view adresowalnym za pośrednictwem właściwości. self.imageViewWtedy wszystko, czego potrzebujesz, to:

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

Gotowe.

Aby to zrobić w Swift, wygląda to tak:

    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 i 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
Pozostałe odpowiedzi są poprawne, ale nieaktualne (i / lub zbyt szczegółowe).
— Steven Kramer

3
Nie ma potrzeby wykonywania przejścia w superwidoku. Udało mi się to zrobić, określając sam UIImageView jako cel.
— Desmond,

7
@ user577888 to tylko drobiazg. Powinieneś przekazać nil do pustego bloku uzupełniania. Bloki nie są wskaźnikami funkcji (jak oznaczono przez ^) i działają jak obiekt-c. Jeśli przekażesz NULL, kompilator zinterpretuje to jako 0 lub (void *) 0. Jeśli z jakiegoś powodu kod bazowy dla transitWithView: ... przypadkowo wyśle ​​komunikat do bloku uzupełniania (np. [Kopia uzupełniająca]) bez sprawdzenia jego poprawności, doprowadzi to do błędu. Dlatego zawsze powinieneś używać wartości zero celu-c, kiedy ustawiasz blok na pusty.
— Pan T

3
@ Mr.T NULL i nil mają identyczną wartość. Obie są zdefiniowane na 0 i prawdopodobnie nigdy się nie zmienią. Dlatego bezpieczne jest używanie NULL wszędzie tam, gdzie chcesz lub mógłbyś użyć nil.
— ashevin

1
@ Mr.T Chodzi mi o to, że używanie jednego nad drugim nigdy nie spowoduje awarii, ponieważ na poziomie skompilowanego kodu są one identyczne i ze względów praktycznych zawsze będą. Pomijanie ostrzeżeń kompilatora jest ogólnie dobrym pomysłem, ale mówienie ludziom, że używanie NULL zamiast nil jest błędem, jest po prostu błędne.
— ashevin


6

Tak, to, co mówisz, jest całkowicie poprawne i to jest sposób na zrobienie tego. Napisałem tę metodę i zawsze używam jej do znikania obrazu. Zajmuję się CALayertym. W tym celu musisz zaimportować główną animację.

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

Możesz zrobić coś odwrotnego w przypadku zanikania obrazu. Po zniknięciu. Po prostu usuwasz to z superview (czyli UIImageView). [imageView removeFromSuperview].


Dodaję ten kod po ustawieniu obrazu w UIImageView, wydaje się, że podczas uruchamiania animacji miga.
— Autoboty

4

Możesz również spakować funkcję zanikania w podklasie, aby można ją było następnie użyć jako wspólnego UIImageView, jak w poniższym przykładzie:

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

Następuje możliwa implementacja.

Uwaga: sposób, w jaki faktycznie zaimplementujesz przejście setImage:funkcji, może się zmienić i może być jednym z innych doskonałych przykładów opisanych w innych odpowiedziach na to pytanie - tworzenie dodatkowego w locie, UIImageViewtak jak tutaj, może być niedopuszczalnym narzutem w twojej konkretnej sytuacji .

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

Powyższy kod opiera się na kilku założeniach (w tym ARC jest włączony w twoim projekcie XCode), ma służyć jedynie jako dowód słuszności koncepcji iw interesie jasności i skupienia pozostaje aktualny, pomijając ważny, niepowiązany kod. Proszę, nie kopiuj i wklejaj na ślepo.


3

Potrzebowałem, aby przejście powtarzało się w nieskończoność. To wymagało DUŻO prób i błędów, ale w końcu uzyskałem efekt końcowy, którego szukałem. Są to fragmenty kodu służące do dodawania animacji obrazu do UIImageView w UITableViewCell.

Oto odpowiedni kod:

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

Cześć. Zastanawiałem się, czy możesz dodać więcej informacji na temat twojego [self setVarietyImageView: cell.imageView]; metoda, ponieważ mogę uruchomić ostatnią komórkę do animacji lub wszystkie do animacji, ale bardzo szybko, dzięki
— gav

Cześć Gav, „varienetImageView” to słabe odniesienie w kontrolerze widoku, więc mogę przypisać do cell.imageView i odwołać się do niego później w metodzie „animateImages”. @property (nonatomic, słaby) UIImageView * variationImageView; Możesz przyspieszyć obracanie obrazów, ustawiając krótszy „czas trwania” w opcji „przejścieWithView”. Mam nadzieję, że o to pytasz.
— Christopherze

Cześć Christopherze, dziękuję za odpowiedź, zastanawiałem się nad „setVarietyImageView:” Zakładam, że jest to metoda void, która pozwala na ponowne użycie, ponieważ udało mi się ożywić tylko ostatnią komórkę. Jeszcze raz dziękuję za odpowiedź, wszystkiego najlepszego Gav
— Jeszcze gav

Ta metoda jest generowana przy użyciu standardowej deklaracji „właściwości”. Na przykład @property (nonatomic, słaby) UIImageView * różnorodnośćImageView. Jest to „słabe” odniesienie, ponieważ odwołuje się do innej zmiennej lokalnej (imageView komórki tabeli). Zamiast tego mogłem utworzyć odwołanie do komórki tabeli i uzyskać dostęp do cell.imageView w metodzie animateImages. Mam nadzieję, że to pomoże.
— Christopher,

2

Po zabawie UIView.transition()i problemach z .transitionCrossDissolveopcją (próbowałem animować obrazy zmieniające się w jednym UIImageView i przejście następowało natychmiast bez animacji) stwierdziłem, że wystarczy dodać jeszcze jedną opcję, która pozwala animować właściwości zmieniające się w widoku ( Swift 4.2 ):

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

Ponadto: jeśli masz jakiekolwiek podwydy na swoim imageView, zostanie on również przerysowany i może to uniemożliwić animację. Na przykład miałem subview z rozmyciem na moim imageView iw takim przypadku animacja nie działa. Po prostu zmieniłem hierarchię widoku i przesunąłem rozmycie do własnego widoku i umieściłem go nad imageView.


0

Myślę, że to najkrótszy sposób na zrobienie tego. Utwórz animację UIView i zatwierdź ją w swoim imageView.

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

0

Korzystając z tej highlightedImagewłaściwości, można to nieco uprościć. Oto przykład w Swift 3. Najpierw ustaw zarówno normalny, jak i podświetlony obraz:

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

A kiedy chcesz przełączać się między tymi animowanymi:

UIView.transition(with: imageView, duration: 0.3, options: .transitionCrossDissolve, animations: { self.imageView.isHighlighted = !self.imageView.isHighlighted}, completion: .none)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.