首页 > 代码库 > 【iOS开发-45】Tom猫案例:动画、imageNamed与imageWithContentOfFile对内存影响、图片文件夹放哪儿以及文档注释
【iOS开发-45】Tom猫案例:动画、imageNamed与imageWithContentOfFile对内存影响、图片文件夹放哪儿以及文档注释
今天tom猫案例效果:
(1)最傻最笨的办法:
——所有的点击都是按钮,只不过有6个有图标的按钮,有些头部、左右脚、肚子、尾巴那块也是一个按钮,只不过没背景没文字没边框的按钮用户按不到而已。
——这里的帧动画核心是UIImageView对象的一个属性animationImages,这个属性里面是以数组形式存放的图片。当然还有个重要的方法startAnimating用来播放前面那个属性里面的图片,就形成动画。再当然一下,还有设置时间和播放次数的属性。
注意点:
——我们一般把图片放在Images.xcassets里面,而且无论是png格式还是jpg格式貌似都可以省略后缀。
——目前为止,几乎只使用了一个代码简化方法,就是格式化输出,用%02d表示00、01......18、19......这些序列,从而导入众多的图片。
//使用3.5英寸屏幕模拟效果更好 //无论是jpg还是png,在引用文件时都可以省略图片后缀 #import "ViewController.h" @interface ViewController () @property(nonatomic,retain) UIImageView *imgView1; @end @implementation ViewController - (void)viewDidLoad { //中间的UIImageView,可以静态显示图片,下面动态播放帧动画也是用它 self.imgView1=[[UIImageView alloc]init]; self.imgView1.frame=CGRectMake(0, 0, 320, 512); self.imgView1.image=[UIImage imageNamed:@"angry_00.jpg"]; self.imgView1.contentMode=UIViewContentModeScaleAspectFit; [self.view addSubview:self.imgView1]; //定义6个看得见的按钮分列两边,另外还有头、左右脚、肚子、尾巴5个隐形按钮提供点击 UIButton *btnCymbal=[[UIButton alloc]init]; btnCymbal.frame=CGRectMake(5, 260, 60, 60); [btnCymbal setImage:[UIImage imageNamed:@"cymbal.png"] forState:UIControlStateNormal]; [btnCymbal addTarget:self action:@selector(cymbalClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnCymbal]; UIButton *btnDrink=[[UIButton alloc]init]; btnDrink.frame=CGRectMake(5, 340, 60, 60); [btnDrink setImage:[UIImage imageNamed:@"drink.png"] forState:UIControlStateNormal]; [btnDrink addTarget:self action:@selector(drinkClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnDrink]; UIButton *btnEat=[[UIButton alloc]init]; btnEat.frame=CGRectMake(5, 420, 60, 60); [btnEat setImage:[UIImage imageNamed:@"eat.png"] forState:UIControlStateNormal]; [btnEat addTarget:self action:@selector(eatClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnEat]; UIButton *btnFart=[[UIButton alloc]init]; btnFart.frame=CGRectMake(255, 260, 60, 60); [btnFart setImage:[UIImage imageNamed:@"fart.png"] forState:UIControlStateNormal]; [btnFart addTarget:self action:@selector(fartClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnFart]; UIButton *btnPie=[[UIButton alloc]init]; btnPie.frame=CGRectMake(255, 340, 60, 60); [btnPie setImage:[UIImage imageNamed:@"pie.png"] forState:UIControlStateNormal]; [btnPie addTarget:self action:@selector(pieClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnPie]; UIButton *btnScratch=[[UIButton alloc]init]; btnScratch.frame=CGRectMake(255, 420, 60, 60); [btnScratch setImage:[UIImage imageNamed:@"scratch.png"] forState:UIControlStateNormal]; [btnScratch addTarget:self action:@selector(scratchClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnScratch]; UIButton *btnLeftFoot=[[UIButton alloc]init]; btnLeftFoot.frame=CGRectMake(115, 455, 40, 25); [btnLeftFoot addTarget:self action:@selector(leftFootClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnLeftFoot]; UIButton *btnRightFoot=[[UIButton alloc]init]; btnRightFoot.frame=CGRectMake(160, 455, 40, 25); [btnRightFoot addTarget:self action:@selector(rightFootClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnRightFoot]; UIButton *btnHead=[[UIButton alloc]init]; btnHead.frame=CGRectMake(80, 90, 160, 160); [btnHead addTarget:self action:@selector(headClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnHead]; UIButton *btnStomach=[[UIButton alloc]init]; btnStomach.frame=CGRectMake(100, 330, 120, 120); [btnStomach addTarget:self action:@selector(stomachClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnStomach]; UIButton *btnTail=[[UIButton alloc]init]; btnTail.frame=CGRectMake(216, 380, 30, 80); [btnTail addTarget:self action:@selector(tailClick) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btnTail]; [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } -(void)cymbalClick{ //先创建一个可变数组,并用for循环添加图片,因为图片太多,一个个添加会死人的。 NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<13; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"cymbal_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } //把这个图片数组赋值给我们之前定义的UIImageView对象imgView1 self.imgView1.animationImages=muArr1; //以下是设置帧动画的播放时间和播放次数,最后记得开始这个动画,其他类似相同 self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=muArr1.count*0.1; [self.imgView1 startAnimating]; } -(void)drinkClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<81; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"drink_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=muArr1.count*0.1; [self.imgView1 startAnimating]; } -(void)eatClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<40; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"eat_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)fartClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<28; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"fart_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)pieClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<24; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"pie_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)scratchClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<56; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"scratch_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)leftFootClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<30; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"footLeft_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)rightFootClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<30; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"footRight_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)headClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<81; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"knockout_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)stomachClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<34; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"stomach_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)tailClick{ NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<26; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"angry_%02d.jpg",i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } @end
(2)补全漏洞:动画一个动画的时候点击另一个按钮失效,即保证播放完当前动画才能操作下一个。
即,在每一个点击事件的方法代码中添加如下,判断这UIImageView的对象imaView1如果正在播放,那么直接返回,不做任何操作,否则,执行一次动画操作。
[self playTomWithName:@"stomach" Number:34];
(3)合并代码:相同的直接合并,不同的用参数传递来调整,即把11个点击方法重写成如下:
-(void)playTomWithName:(NSString *)name Number:(int)number{ if (self.imgView1.isAnimating) return; NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<number; i++) { UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"%@_%02d.jpg",name,i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; } -(void)cymbalClick{ [self playTomWithName:@"cymbal" Number:13]; } -(void)drinkClick{ [self playTomWithName:@"drink" Number:81]; } -(void)eatClick{ [self playTomWithName:@"eat" Number:40]; } -(void)fartClick{ [self playTomWithName:@"fart" Number:28]; } -(void)pieClick{ [self playTomWithName:@"pie" Number:24]; } -(void)scratchClick{ [self playTomWithName:@"scratch" Number:56]; } -(void)leftFootClick{ [self playTomWithName:@"footLeft" Number:30]; } -(void)rightFootClick{ [self playTomWithName:@"footRight" Number:30]; } -(void)headClick{ [self playTomWithName:@"knockout" Number:81]; } -(void)stomachClick{ [self playTomWithName:@"stomach" Number:34]; } -(void)tailClick{ [self playTomWithName:@"angry" Number:26]; }
(4)内存管理,imageNamed加载图像的弊端。
我们运行上述代码后,发现,多点击几次不同的按钮播放动画后,内存使用急剧上升。主要原因在于imageNamed加载图片的方法:它加载完图片后都存放在内存中,方便下次使用,所以内存中不断新增图片,会很大,在手机上会出现因为内存过载而闪退的现象。
点了几个按钮后,内存情况如下:
解决办法,用其他方法加载图片,用imageWithContentOfFile方法:这种方法不会将图片都加载到内存中,用完就释放了。
-(void)playTomWithName:(NSString *)name Number:(int)number{ if (self.imgView1.isAnimating) return; NSMutableArray *muArr1=[[NSMutableArray alloc]init]; for (int i=0; i<number; i++) { NSString *fileName=[NSString stringWithFormat:@"%@_%02d.jpg",name,i]; NSBundle *bundle1=[NSBundle mainBundle]; NSString *path=[bundle1 pathForResource:fileName ofType:nil]; UIImage *tmpImg=[UIImage imageWithContentsOfFile:path]; // UIImage *tmpImg=[UIImage imageNamed:[NSString stringWithFormat:@"%@_%02d.jpg",name,i]]; [muArr1 addObject:tmpImg]; } self.imgView1.animationImages=muArr1; self.imgView1.animationRepeatCount=1; self.imgView1.animationDuration=1; [self.imgView1 startAnimating]; }
只是不会把所有的都加载,只是当前播放的那组图片会加载进来而已。
(5)再一次清除内存——再播放完动画后清除内存(其实也可以叫缓存)。
核心在于:要想清除缓存,只需要把self.imgView1.animationImages=nil;那么就没有强指针指向muArr1,muArr1就会被回收。
但,我们不能直接用上述语句,因为使用上述语句,会发现,动画还没有播放就已经被停止了,因为动画播放有一个时间持续,而执行上述的代码只需要极短时间。
所以?我们需要延迟执行上面的语句。延迟多少时间?比动画播放时间长1秒钟,差不多。
除了self.imgView1.animationImages=nil;之外,还可以利用方法进行nil赋值,语句是[self.imgView1 setAnimationImages:nil];
而延迟执行一个方法,正好有performSelector...afterDelay这个方法,所以:
double delayNum=(double)self.imgView1.animationDuration+1.0; [self.imgView1 performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:delayNum];
(6)关于图片放在文件夹,文件夹直接拖放到Xcode中得问题。
直接的图片建议拖放到Images.xcassets中,但是如果是一个文件夹,不建议直接拖放到Images.xcassets,因为用mainBundle查找文件时查找不到。建议拖放到Supporting Files中。从两边文件夹颜色可分辨。总的来说:
——黄颜色的那个文件夹其实不能称之为文件夹,我们可以叫它组,它只是把图片分组而已,因为图片全部都被统一放在mainBudle中,而不是在这些“文件夹”中。
——蓝颜色的那个文件夹就是真正的文件夹,它是一种路径,如果要访问它里面的图片,那么我们需要写出这个文件夹。所以对我们用mainBundle查找文件十分不方便。
(7)补充:文档注释的写法
文档注释和普通注释不一样,文档注释更强大,不仅方便阅读,而且还提供动态输入时候的提示,即如下效果:
要实现这个效果,注释的格式如下,不管哪种方式,都是前面是/**后面是*/:
//写在一行 /**HELLO,这里是文档注释*/ //写在多行 /** HELLO,这里是文档注释 */
不要和普通注释的/* */混淆。
【iOS开发-45】Tom猫案例:动画、imageNamed与imageWithContentOfFile对内存影响、图片文件夹放哪儿以及文档注释