Подтвердить что ты не робот

Анимация с использованием массива изображений в последовательности

У меня есть массив изображений, которые я хочу оживить, играя эти изображения один за другим в последовательности. Я хочу повторить весь цикл несколько раз. Я разрабатываю игру для iPad. Предложите мне способ достижения этой функциональности в Objective-C с помощью рамки Cocoa.

4b9b3361

Ответ 1

См. свойство animationImages UIImageView. Его трудно сказать, соответствует ли оно вашим потребностям, поскольку вы не даете нам подробностей, но это хорошее начало.

Ответ 2

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

добавьте свои собственные изображения в массив .repeat Count 0 означает бесконечный цикл. Вы также можете указать свой собственный номер.

Ответ 3

Существует как минимум 3 способа анимации массива изображений с помощью UIImageView. Я добавляю 3 ссылки для загрузки образца кода для 3 возможностей.

Первый - это тот, который все знают. Другие менее известны.

- UIImageView.animationImages
Пример ссылки

Проблема этого заключается в том, что делегату не нужно сообщать нам, в какой момент анимация завершена. Таким образом, у нас могут быть проблемы, если мы хотим отобразить что-то после анимации.

Таким же образом невозможно автоматически сохранить последнее изображение из анимации в UIImageView. Если мы объединим обе проблемы, у нас может быть пробел в конце анимации, если мы хотим сохранить последний кадр на экране.

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
Пример ссылки

Этот способ анимации работает через CAAnimation. У него есть удобный делегат для использования, и мы можем знать, когда закончится анимация.

Это, вероятно, лучший способ анимации массива изображений.

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

Делегат:

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

- CADisplayLink
Пример ссылки

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

Этот способ сделать это действительно интересен и открывает множество возможностей манипулировать тем, что мы показываем на экране.

GetLink getter:

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

    return _displayLink;
}

Методы:

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

ОБЩАЯ ПРОБЛЕМА:

Несмотря на то, что у нас есть три возможности, если ваша анимация имеет много больших изображений, подумайте об использовании видео. Использование памяти значительно уменьшится.

Общая проблема, с которой вам придется столкнуться, заключается в момент выделения изображений.

Если вы используете [UIImage imageNamed: @ "imageName" ], у вас возникнут проблемы.

От 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.

Итак, imageNamed: хранит изображение в приватном кеше.
- Первая проблема заключается в том, что вы не можете контролировать размер кеша.
- Вторая проблема заключается в том, что кеш не очищался вовремя, и если вы выделяете много изображений с помощью imageNamed:, ваше приложение, вероятно, выйдет из строя.

РЕШЕНИЕ:

Выделите изображения непосредственно из 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];

Маленькая проблема:

Изображения в Images.xcassets никогда не выделяются. Итак, переместите изображения вне Images.xcassets, чтобы выделить их непосредственно из Bundle.

Ответ 4

Лучшее решение для меня использует CADisplayLink. UIImageView не имеет блока завершения, и вы не можете поймать шаги анимации. В моей задаче я должен постепенно менять фон представления с помощью секвенсора изображений. Таким образом CADisplayLink позволяет обрабатывать шаги и завершать анимацию. Если мы говорим об использовании памяти, я думаю, что лучшее решение загружает изображения из набора и удаления массива после завершения

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

Ответ 5

Я добавил расширение для быстрого

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

        })
    }

}