动画使用顺序的图像阵列

时间:2011-05-18 06:05:32

标签: objective-c cocoa-touch ios animation

我有一组图像,我希望通过在序列中依次播放这些图像来制作动画。我想多次重复整个循环。我正在为iPad开发游戏。建议使用Cocoa框架在Objective-C中实现此功能的方法。

6 个答案:

答案 0 :(得分:50)

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

在array.repeat中添加您自己的图像。计数0表示无限循环。您也可以给出自己的数字。

答案 1 :(得分:18)

至少有3种方法可以通过UIImageView为图像数组制作动画。我正在添加3个链接来下载3种可能性的示例代码。

第一个是每个人都知道的。其他人鲜为人知。

- UIImageView.animationImages
Example Link

这个的问题是没有委托告诉我们动画完成的时刻。因此,如果我们想要在动画之后显示某些内容,我们就会遇到问题。

以同样的方式,不可能自动保留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
Example Link

这种通过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
Example Link

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

这样做非常有趣,并且可以操作我们在屏幕上显示的内容。

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

一般问题:

即使我们有这3种可能性,如果你的动画有很多大图像,请考虑使用视频。内存的使用会减少很多。

您将要面对的一般问题是在分配图像的那一刻。

如果您使用[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:分配大量图像,您的应用程序可能会崩溃。

<强> SOLUTION:

直接从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分配。

答案 2 :(得分:11)

查看UIImageViewanimationImages属性。很难说它是否符合您的需求,因为您没有向我们提供详细信息,但这是一个良好的开端。

答案 3 :(得分:2)

我为此添加了一个swift 3.0扩展程序

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

        })
    }

}

答案 4 :(得分:1)

我使用CADisplayLink的最佳解决方案。 UIImageView没有完成块,你无法捕捉到动画的步骤。在我的任务中,我必须逐步改变图像序列的背景。因此,CADisplayLink允许您处理步骤和完成动画。如果我们谈论内存的使用,我认为最佳解决方案在完成

后从bundle和delete数组加载图像

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 :(得分:0)

这是一个简单而有效的动画代码。/

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