UIKit之汤姆猫Demo

需求:

实现动态tom猫案例。
在这里插入图片描述

分析

tom猫的动画本质多张图片快速播放,大概几十张。背景是图片框。涉及组件有:按钮、图片视图类UIImageView 。
需要实现功能是图片快速切换。涉及动画,与之前案例的放大、缩小不同。
其中,点击Tom猫的头能触发动画的本质是有一个没有文字和没有背景图片的UIButton。

动画播放相关的API:
animationImages:需要播放的图片组数
animationDuartion:动画持续时间
animationRepeatCount:执行次数
startAnimating:开始执行
stopAnimating:停止执行
isAnimating:判断是否在执行状态

要播放的图片组所有名称需要先用NSArray拼接,然后传给UIImage类实例,再把实例再传入相关动画的API。

实现:

  1. 创建组件:
    多个可见按钮(带显示图的)、头部的按钮(没有文字和图片)、放tom猫的背景图视图。
@interface TomView : UIView
@property(nonatomic, strong) UIImageView* imgView;
@property(nonatomic, strong) UIButton* headbtn;
@property(nonatomic, strong) UIButton* btn1;
@property(nonatomic, strong) UIButton* btn2;
@property(nonatomic, strong) UIButton* btn3;
@property(nonatomic, strong) UIButton* btn4;
@property(nonatomic, strong) UIButton* btn5;
@property(nonatomic, strong) UIButton* btn6;
@end

  1. 初始化背景、组件
    1> 初始化背景猫,该图片填充满整个视图。
    2> 左侧三个可视按钮。
    3> 添加每个按钮的事件
    4> 加入子视图
-(instancetype) initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
    	// 初始化背景图
        _imgView = [[UIImageView alloc] initWithFrame: self.bounds];  // self.view.bounds:本身是vie为,所以用self即可
        _imgView.contentMode = UIViewContentModeScaleAspectFit;
        _imgView.image = [UIImage imageNamed:@"angry_00.jpg"];
        _imgView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        _headbtn = [[UIButton alloc] initWithFrame:CGRectMake(85, 200, 220, 220)];
        // 三个可视按钮
        _btn1 = [[UIButton alloc] initWithFrame:CGRectMake(30, 500, 50, 50)];
        _btn2 = [[UIButton alloc] initWithFrame:CGRectMake(30, 570, 50, 50)];
        _btn3 = [[UIButton alloc] initWithFrame:CGRectMake(30, 640, 50, 50)];
        
        // 隐藏头部的按钮
        [_btn1 setBackgroundImage:[UIImage imageNamed:@"drink.png"] forState:UIControlStateNormal];
        [_btn2 setBackgroundImage:[UIImage imageNamed:@"fart.png"] forState:UIControlStateNormal];
        [_btn3 setBackgroundImage:[UIImage imageNamed:@"eat.png"] forState:UIControlStateNormal];
        
        [_headbtn addTarget:self action:@selector(head1) forControlEvents:UIControlEventTouchUpInside];
        [_btn1 addTarget:self action:@selector(drink1) forControlEvents:UIControlEventTouchUpInside];
        [_btn2 addTarget:self action:@selector(fart1) forControlEvents:UIControlEventTouchUpInside];
		[_btn3 addTarget:self action:@selector(eat) forControlEvents:UIControlEventTouchUpInside];
		
        [self addSubview:_imgView];
        [self addSubview:_btn1];
        [self addSubview:_btn2];
        [self addSubview:_btn3];
        [self addSubview:_headbtn];
    }
    return self;
}
  1. 动画的实现
    tom猫有多种动作:喝奶、拍脑袋就晕、放P,这些动作的本质都是加载图片,将图片赋值给动画API。具体过程如下:
    1> 图片加载到数组中
    2> 设置UIImageView的animationImages属性
    3> 动画持续时间
    4> 重复次数
    5> 启动动画

以下提供了三个函数的拆分写法以及封装写法,在上面的按钮绑定的事件函数为封装后的代码。
注意加载图片是否会占用内存,初始化UIImage类有两种方法。

#import "TomView.h"

@implementation TomView
// 对三个函数进行拆分写法
-(void) drink{
    // 旧的方式:图片加载到数组,使用缓存,不建议这么实现,图片太多,全加载到内存中占用太大
    // 将图片名称全部读取出:由于图片资源序号占两位,所以用字符串填充
    // 把图片全装入数组,用可变数组,可以装任意内容
    NSMutableArray *arrayM = [NSMutableArray array];
    for(int i = 0; i <= 80; i++){
        NSString *imgName = [NSString stringWithFormat:@"drink_%02d.jpg", i];
        UIImage *imgCat = [UIImage imageNamed:imgName];
        [arrayM addObject:imgCat];
    }
    // 1. 图片数组赋值给动画API:
    self.imgView.animationImages = arrayM;
    // 2 持续时间
    self.imgView.animationDuration = self.imgView.animationImages.count * 0.1;
    // 3. 设置是否重复播放
    self.imgView.animationRepeatCount = 1;
    // 4. 开启动画
    [self.imgView startAnimating];
}

-(void) fart{
    // bundle方式,不占缓存:要求是资源文件不能放在Assets中
    //[];
    NSMutableArray *arrayM = [NSMutableArray array];
    for(int i = 0; i <= 27; i++){
        NSString *imgName = [NSString stringWithFormat:@"fart_%02d.jpg", i];
        // 通过图片名称去App安装路径中寻找资源文件
        NSString *path = [[NSBundle mainBundle] pathForResource:imgName ofType:nil];
        // 以路径获得图片对象:从磁盘上读速度虽然慢,但是不占内存
        UIImage *imgCat = [UIImage imageWithContentsOfFile:path];
        [arrayM addObject:imgCat];
    }
    // OC中:UIImage加载图片时有两种主要方法:imageWithContentsOfFile: 和 imageNamed:
    // 如drink函数中用UIImage的方式会使UIImage 会留在内存中,
    // 且点击新按钮,会不断加到内存中去
    // 1. 设置UIImageView
    self.imgView.animationImages = arrayM;
    // 2. 动画时长
    self.imgView.animationDuration = self.imgView.animationImages.count*0.1;
    // 3. 重复次数
    self.imgView.animationRepeatCount = 1;
    // 4. 开启
    [self.imgView startAnimating];
    
    // 清除缓存的方式探究::
    // 存图片的数组中仍然占内存 释放图片数组内存的方式
    // 4. 直接销毁会因为运行太快动画没显示就把数组清空了
    // 以下不可取
    //self.imgView.animationImages = nil;
    
    // 如果做判断才销毁则不会销毁,因为那一时刻监测到正在执行
    // 以下不可取
    //    if(!self.imgView.isAnimating){
    //        self.imgView.animationImages = nil;
    //    }
    // 所以有专门的动画延迟释放API:
    // 停顿3秒后销毁的API:
    [self.imgView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:self.imgView.animationImages.count*0.1];
    //
}

-(void) head
{
    // 1. 图片数组
    NSMutableArray *arrayM = [NSMutableArray array];
    // 2 根据名称图片、利用bundle获取所有图片并添加到array中
    for(int i = 0; i <= 80; i++){
        NSString *imgName = [NSString stringWithFormat:@"knockout_%02d.jpg", i];
        // 通过图片名称去App安装路径中寻找资源文件
        NSString *path = [[NSBundle mainBundle] pathForResource:imgName ofType:nil];
        // 以路径获得图片对象:从磁盘上读速度虽然慢,但是不占内存
        UIImage *imgCat = [UIImage imageWithContentsOfFile:path];
        [arrayM addObject:imgCat];
    }
    // 动画设置
    self.imgView.animationImages = arrayM;
    // 动画时长
    self.imgView.animationDuration = self.imgView.animationImages.count*0.1;
    // 重复次数
    self.imgView.animationRepeatCount = 1;
    // 开启
    [self.imgView startAnimating];
    
    // 清空数组,释放内存
    [self.imgView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:self.imgView.animationImages.count*0.1];
    
}

- (void) drink1{
    [self startAnimation:80 picName:@"drink"];
}

- (void) fart1{
    [self startAnimation:27 picName:@"fart"];
}

- (void) head1{
    [self startAnimation:80 picName:@"knockout"];
    
}
// 封装写法:动画执行次数、需要在路径中寻找的名称
-(void) startAnimation:(int)count picName:(NSString *)btnName
{
    // 当动画正在执行,点击其它按钮应该无反应
    if(self.imgView.isAnimating){
        return;
    }
    // 1. 把图片加载到数组中
    NSMutableArray *arrayM = [NSMutableArray array];
    // 2. 拼接图片名称
    for(int i = 0 ; i <= count; i++)
    {
        NSString *imgName = [NSString stringWithFormat:@"%@_%02d.jpg", btnName, i];
        // 从手机安装路径中读取该名的图片资源为对象,不占内存
        NSString *path = [[NSBundle mainBundle] pathForResource:imgName ofType:nil];
        //
        UIImage *imgCat = [UIImage imageWithContentsOfFile:path];
        //
        [arrayM addObject:imgCat];
    }
    // 设置作为动画的图片数组
    self.imgView.animationImages = arrayM;
    // 3. 动画持续时间
    self.imgView.animationDuration = 3;
    // 4. 重复次数
    self.imgView.animationRepeatCount = 1;
    // 5. 启动动画:
    [self.imgView startAnimating];
    // 6. 清空缓存
    [self.imgView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:self.imgView.animationImages.count*0.1];
}
@end

注意问题:

  1. 动画的内存占用:
    初始化图片类有两种方式,一种根据图片名直接加载到内存中,速度较快,但不推荐。因为动画本质是一组图片,内存中图片会越来越多
    使用NSBundle方式。不要使用缓存:直接从资源文件的路径中去获取即可。
    注意:用bundle方式,需要把图片资源放到文件夹下,放在Assets中找不到。因为在App中,Assets中的文件不在主目录下。
    该Demo中动画API和前面动画API的写法均不同,前面用了首尾式、block式。

· 以下是如果不适用bundle加载图片会导致项目占用内存过大的效果图。
在这里插入图片描述

  1. 最后一组图片内存释放问题
    最后一组图片加载后还会保存在内存中,需要清空,但清空需要注意延迟几秒后做
    用if(运行状态)来检测后清空,清空函数不会执行。所以需要延迟等动画执行完再情况图片有专门的延迟函数。
    以下是几种清除思路,只有最后一种正确:使用特定API。
// 清除缓存的方式探究::
// 1. 存图片的数组中仍然占内存 释放图片数组内存的方式
//  . 直接销毁会因为运行太快动画没显示就把数组清空了
//  不可取
//self.imgView.animationImages = nil;

//  2. 如果做判断才销毁则不会销毁,因为那一时刻监测到正在执行
// 以下不可取
//    if(!self.imgView.isAnimating){
//        self.imgView.animationImages = nil;
//    }
// 3 . 专门的动画延迟释放API:在代码中有
//  停顿3秒后销毁的API:
  1. tom猫背景没显示,后缀赋值错了
  1. 动画图片加载报错:
    无非就是图片数量没对应上。
  1. 初始化某控件没显示出来:
    仔细查看subView。
  1. 发现动画没播放:
    没有使用播放动画函数。
  1. 报错: No visible @interface for ‘TomView’ declares the selector ‘startAnimation1:picName:’
    在一个自定义函数调用另外一个自定义函数时,因为命名错了。(可以不声明)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值