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?

Josh Kahane
źródło
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

Mirkules
źródło
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)
glony
źródło
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].

Srikar Appalaraju
źródło
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.

magma
źródło
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;
}
Krzysztof
źródło
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.

Pavel
źródło
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];
divyenduz
źródło
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)
Jens Schwarzer
źródło