按顺序使用图像数组的动画

发布于 2024-11-08 03:14:03 字数 103 浏览 0 评论 0原文

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

I have an array of images which I want to animate by playing these images one after the other in a sequence. I want to repeat the whole loop several times. I am developing a game for iPad. Suggest to me a method to achieve this functionality in Objective-C with the Cocoa framework.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

没有心的人 2024-11-15 03:14:03
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 表示无限循环。您也可以给出自己的编号。

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

add your own images in the array.repeat Count 0 means infinite loop.You can give your own number also.

活雷疯 2024-11-15 03:14:03

至少有 3 种方法可以通过 UIImageView 对图像数组进行动画处理。我添加了 3 个链接来下载 3 种可能性的示例代码。

第一个是大家都知道的。其他的则鲜为人知。

- UIImageView.animationImages
示例链接

这个的问题是没有 Delegate 告诉我们动画在哪一刻完成。因此,如果我们想在动画之后显示某些内容,可能会遇到问题。

同样,不可能自动将动画中的最后一个图像保留在 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
示例链接

CADisplayLink 对象是一个计时器对象,允许您的应用程序将其绘制与刷新同步显示速率。

这种方法非常有趣,并且为操纵我们在屏幕上显示的内容提供了很多可能性。

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:

此方法在系统缓存中查找具有指定名称的图像对象,并返回该对象(如果存在)。如果缓存中尚不存在匹配的图像对象,则此方法将从磁盘或资产类别中查找并加载图像数据,然后返回结果对象。您不能假设此方法是线程安全的。

因此,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 分配。

There are at least 3 ways to animate an array of images through a UIImageView. I'm adding 3 links to download sample code for the 3 possibilities.

The first one is the one that everyone knows. The other ones are less known.

- UIImageView.animationImages
Example Link

The problem of this one is that do not have Delegate to tell us in which moment the animation is finished. So, we can have problems if we want to display something after the animation.

In the same way, there is no possibility to kept the last image from the animation in the UIImageView automatically. If we combine both problems we can have a gap at the end of the animation if we want to kept the last frame on screen.

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

This way to animate works through CAAnimation. It have an easy delegate to use and we can know when the animation finish.

This is probably the best way to animate an array of 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"];
}

Delegate:

- (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.

This way to do it is really interesting and opens a lot of possibilities to manipulate what are we showing in screen.

DisplayLink getter:

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

    return _displayLink;
}

Methods:

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

GENERAL PROBLEM:

Even though we have this 3 possibilities, if your animation is with a lot of big images, consider using a video instead. The usage of memory will decrease a lot.

A General problem you will face doing this is in the moment of the allocation of the images.

If you use [UIImage imageNamed:@"imageName"] you will have cahe problems.

From 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.

So, imageNamed: stores the image in a private Cache.
- The first problem is that you can not take control of the cache size.
- The second problem is that the cache did not get cleaned in time and if you are allocating a lot of images with imageNamed:, your app, probably, will crash.

SOLUTION:

Allocate images directly from 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];

Small problem:

Images in Images.xcassets get never allocated. So, move your images outside Images.xcassets to allocate directly from Bundle.

小苏打饼 2024-11-15 03:14:03

请参阅 UIImageView。很难说它是否符合您的需求,因为您没有向我们提供详细信息,但这是一个良好的开始。

See the animationImages property of UIImageView. It’s hard to say if it fits your needs as you don’t give us details, but it’s a good start.

送舟行 2024-11-15 03:14:03

我为此添加了 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)
            }

        })
    }

}

I have added a swift 3.0 extension for this

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

        })
    }

}
秋叶绚丽 2024-11-15 03:14:03

对我来说最好的解决方案是使用 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

Best solution for me use CADisplayLink. UIImageView doesn't have completion block and you can't catch steps of animation. In my task i must changing background of view with image sequencer step by step. So CADisplayLink allows you handling steps and finishing animation. If we talk about usage of memory, i think best solution load images from bundle and delete array after finishing

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
蓝海 2024-11-15 03:14:03

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

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

This is a simple and working code for 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!");
                     }];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文