web-dev-qa-db-fra.com

Animation utilisant un tableau d'images en séquence

J'ai une série d'images que je veux animer en les lisant l'une après l'autre dans une séquence. Je veux répéter la boucle entière plusieurs fois. Je développe un jeu pour iPad. Suggérez-moi une méthode pour réaliser cette fonctionnalité dans Objective-C avec le framework Cocoa.

18
Yadnesh

Voir la propriété animationImages de UIImageView . Il est difficile de dire si cela répond à vos besoins, car vous ne nous donnez pas de détails, mais c’est un bon début.

11
zoul
NSArray *animationArray = [NSArray arrayWithObjects:
                          [UIImage imageNamed:@"images.jpg"],
                          [UIImage imageNamed:@"images1.jpg"],
                          [UIImage imageNamed:@"images5.jpg"],
                          [UIImage imageNamed:@"index3.jpg"],
                          nil];
UIImageView *animationView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0,320, 460)];
animationView.backgroundColor      = [UIColor purpleColor];
animationView.animationImages      = animationArray;
animationView.animationDuration    = 1.5;
animationView.animationRepeatCount = 0;
[animationView startAnimating]; 
[self.view addSubview:animationView];       
[animationView release];

ajoutez vos propres images dans le tableau.repeat Le compte 0 signifie une boucle infinie.Vous pouvez également donner votre propre numéro.

50
Gypsa

Il y a au moins 3 façons d'animer un tableau d'images à l'aide d'une UIImageView. J'ajoute 3 liens pour télécharger un exemple de code pour les 3 possibilités.

Le premier est celui que tout le monde connaît. Les autres sont moins connus.

- UIImageView.animationImages
Exemple de lien

Le problème de celui-ci est que vous n’avez pas à déléguer pour nous dire à quel moment l’animation est terminée. Nous pouvons donc avoir des problèmes si nous voulons afficher quelque chose après l'animation. 

De la même manière, il n’est pas possible de conserver automatiquement la dernière image de l’animation dans la variable UIImageView. Si nous combinons les deux problèmes, nous pouvons avoir un écart à la fin de l'animation si nous voulons conserver la dernière image à l'écran.

self.imageView.animationImages = self.imagesArray; // the array with the images
self.imageView.animationDuration = kAnimationDuration; // static const with your value
self.imageView.animationRepeatCount = 1;
[self.imageView startAnimating];

- CAKeyframeAnimation
Exemple de lien

Cette façon d’animer des œuvres à travers CAAnimation. Il a un délégué facile à utiliser et nous pouvons savoir quand l’animation se termine.

C’est probablement le meilleur moyen d’animer un tableau d’images.

- (void)animateImages
{
    CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
    keyframeAnimation.values = self.imagesArray;

    keyframeAnimation.repeatCount = 1.0f;
    keyframeAnimation.duration = kAnimationDuration; // static const with your value

    keyframeAnimation.delegate = self;

    //    keyframeAnimation.removedOnCompletion = YES;
    keyframeAnimation.removedOnCompletion = NO;
    keyframeAnimation.fillMode = kCAFillModeForwards;

    CALayer *layer = self.animationImageView.layer;

    [layer addAnimation:keyframeAnimation
                 forKey:@"girlAnimation"];
}

Déléguer:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    if (flag)
    {
        // your code
    }
}

- CADisplayLink
Exemple de lien

A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.

Cette façon de faire est vraiment intéressante et ouvre beaucoup de possibilités pour manipuler ce que nous montrons à l'écran.

DisplayLink getter:

- (CADisplayLink *)displayLink
{
    if (!_displayLink)
    {
        _displayLink = [CADisplayLink displayLinkWithTarget:self
                                                   selector:@selector(linkProgress)];
    }

    return _displayLink;
}

Les méthodes:

- (void)animateImages
{
    self.displayLink.frameInterval = 5;
    self.frameNumber = 0;
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop]
                           forMode:NSRunLoopCommonModes];
}

- (void)linkProgress
{
    if (self.frameNumber > 16)
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
        self.animationImageView.image = [UIImage imageNamed:@"lastImageName"];
        self.imagesArray = nil;
        return;
    }

    self.animationImageView.image = self.imagesArray[self.frameNumber++];
    self.frameNumber++;
}

PROBLÈME GÉNÉRAL:

Même si nous avons ces 3 possibilités, si votre animation contient beaucoup de grandes images, envisagez d'utiliser une vidéo. L'utilisation de la mémoire diminuera beaucoup.

Un problème général auquel vous devrez faire face est le moment de l’attribution des images. 

Si vous utilisez [UIImage imageNamed: @ "imageName"], vous rencontrerez des problèmes.

De Apple:

This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method locates and loads the image data from disk or asset catelog, and then returns the resulting object. You can not assume that this method is thread safe.

Donc, imageNamed: stocke l'image dans un cache privé.
- Le premier problème est que vous ne pouvez pas prendre le contrôle de la taille du cache.
- Le deuxième problème est que le cache n'a pas été nettoyé à temps et si vous attribuez beaucoup d'images avec imageNamed:, votre application va probablement se bloquer.

SOLUTION:

Attribuez des images directement à partir de Bundle:

NSString *imageName = [NSString stringWithFormat:@"imageName.png"];
NSString *path = [[NSBundle mainBundle] pathForResource:imageName

// Allocating images with imageWithContentsOfFile makes images to do not cache.
UIImage *image = [UIImage imageWithContentsOfFile:path];

Petit problème:

Les images dans Images.xcassets ne sont jamais allouées. Alors, déplacez vos images en dehors de Images.xcassets pour les allouer directement à partir de Bundle.

18
Gabriel.Massana

J'ai ajouté une extension Swift 3.0 pour cette

extension UIImageView {

    func animate(images: [UIImage], index: Int = 0, completionHandler: (() -> Void)?) {

        UIView.transition(with: self, duration: 0.5, options: .transitionCrossDissolve, animations: {
            self.image = images[index]

        }, completion: { value in
            let idx = index == images.count-1 ? 0 : index+1

            if idx == 0 {
                completionHandler!()

            } else {
                self.animate(images: images, index: idx, completionHandler: completionHandler)
            }

        })
    }

}
2
ZaEeM ZaFaR

La meilleure solution pour moi utilise CADisplayLink. UIImageView n'a pas de bloc d'achèvement et vous ne pouvez pas capturer les étapes d'animation. Dans ma tâche, je dois changer pas à pas l'arrière-plan de la vue avec le séquenceur d'images. Ainsi, CADisplayLink vous permet de gérer les étapes et de terminer l’animation. Si nous parlons d’utilisation de la mémoire, je pense que la meilleure solution consiste à charger les images de l’ensemble et à supprimer le tableau après avoir terminé 

ImageSequencer.h  

typedef void (^Block)(void);

@protocol ImageSequencerDelegate;

@interface QSImageSequencer : UIImageView
@property (nonatomic, weak) id <ImageSequencerDelegate> delegate;

- (void)startAnimatingWithCompletionBlock:(Block)block;
@end

@protocol ImageSequencerDelegate <NSObject>
@optional
- (void)animationDidStart;
- (void)animationDidStop;
- (void)didChangeImage:(UIImage *)image;
@end

ImageSequencer.m  

- (instancetype)init {
    if (self = [super init]) {

        _imagesArray = [NSMutableArray array];

        self.image = [self.imagesArray firstObject];
    }

    return self;
}


#pragma mark - Animation

- (void)startAnimating {
    [self startAnimatingWithCompletionBlock:nil];
}


- (void)startAnimatingWithCompletionBlock:(Block)block {
    self.frameNumber = 0;

    [self setSuccessBlock:block];

    self.displayLink.frameInterval = 5;

    if (self.delegate && [self.delegate respondsToSelector:@selector(animationDidStart)]) {
        [self.delegate animationDidStart];
    }

    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop]
                           forMode:NSRunLoopCommonModes];
}


-(void)stopAnimating {
     self.image = [self.imagesArray lastObject];

    [self.displayLink invalidate];
    [self setDisplayLink:nil];
    Block block_ = [self successBlock];
    if (block_) {
        block_();
    }

    if (self.delegate && [self.delegate respondsToSelector:@selector(animationDidStop)]) {
        [self.delegate animationDidStop];
    }

    [self.imagesArray removeAllObjects];
}


- (void)animationProgress {
    if (self.frameNumber >= self.imagesArray.count) {
        [self stopAnimating];
        return;
    }

    if (self.delegate && [self.delegate respondsToSelector:@selector(didChangeImage:)]) {
        [self.delegate didChangeImage:self.imagesArray[self.frameNumber]];
    }

    self.image = self.imagesArray[self.frameNumber];
    self.frameNumber++;
}


#pragma mark - Getters / Setters

- (CADisplayLink *)displayLink {
    if (!_displayLink){
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationProgress)];
    }

    return _displayLink;
}

- (NSMutableArray<UIImage *> *)imagesArray {
    if (_imagesArray.count == 0) {
        // get images from bundle and set to array
    }
    return _imagesArray;
}

@end
1
Dmitry Skidan

C'est un code simple et fonctionnel pour l'animation./

-(void)move
{
    [UIView animateWithDuration:5
                          delay:0.0
                        options: UIViewAnimationCurveEaseOut
                     animations:^{
                         [_imgbox setFrame:CGRectMake(100, 100, _imgbox.frame.size.width, _imgbox.frame.size.height)];
                     }
                     completion:^(BOOL finished){
                         NSLog(@"Done!");
                     }];
}
0
Sooraj s Nair