我有一组图片,希望通过按顺序播放这些图像来实现动画效果,并且希望重复整个循环多次。我正在为iPad开发游戏。请给我建议,如何使用Objective-C和Cocoa框架实现这个功能。
我有一组图片,希望通过按顺序播放这些图像来实现动画效果,并且希望重复整个循环多次。我正在为iPad开发游戏。请给我建议,如何使用Objective-C和Cocoa框架实现这个功能。
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];
将您自己的图像添加到数组中。重复计数0意味着无限循环。您也可以提供自己的数字。
通过UIImageView
至少有3种方式来对图像数组进行动画。我将添加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
示例链接
CADisplayLink对象是一个计时器对象,允许您的应用程序将绘图与显示器的刷新率同步。
这种方法非常有趣,并且开启了许多操作屏幕显示内容的可能性。
DisplayLink 获取器:
- (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 的说明:
该方法在系统缓存中查找指定名称的图像对象,并返回该对象(如果存在)。如果匹配的图像对象不在缓存中,则此方法将从磁盘或资产目录中查找并加载图像数据,然后返回结果对象。不能假定此方法是线程安全的。
因此,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中分配。
查看UIImageView
的animationImages
属性。由于您没有提供细节,因此很难说它是否符合您的需求,但这是一个很好的开始。
UIImageView
。 - zoulextension 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)
}
})
}
}
对于我来说,最好的解决方案是使用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
这是一个简单且可用于动画的代码。
-(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!");
}];
}