Lottie 动画 官方用法 分析

#import "AnimationExplorerViewController.h"
#import "JSONExplorerViewController.h"
#import "LAQRScannerViewController.h"
#import <Lottie/Lottie.h>

typedef enum : NSUInteger {
  ViewBackgroundColorWhite,
  ViewBackgroundColorBlack,
  ViewBackgroundColorGreen,
  ViewBackgroundColorNone
} ViewBackgroundColor;

@interface AnimationExplorerViewController ()

@property (nonatomic, assign) ViewBackgroundColor currentBGColor;
@property (nonatomic, strong) UIToolbar *toolbar;
@property (nonatomic, strong) UISlider *slider;

/**
 动画承载视图
 */
@property (nonatomic, strong) LOTAnimationView *laAnimation;

@end

@implementation AnimationExplorerViewController

- (void)setCurrentBGColor:(ViewBackgroundColor)currentBGColor {
  _currentBGColor = currentBGColor;
  switch (currentBGColor) {
    case ViewBackgroundColorWhite:
      self.view.backgroundColor = [UIColor whiteColor];
      break;
    case ViewBackgroundColorBlack:
      self.view.backgroundColor = [UIColor blackColor];
      break;
    case ViewBackgroundColorGreen:
      self.view.backgroundColor = [UIColor colorWithRed:50.f/255.f
                                                  green:207.f/255.f
                                                   blue:193.f/255.f
                                                  alpha:1.f];
      break;
    case ViewBackgroundColorNone:
      self.view.backgroundColor = nil;
      break;
    default:
      break;
  }
}

- (void)viewDidLoad {
  [super viewDidLoad];
  
  self.currentBGColor = ViewBackgroundColorWhite;
  self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectZero];
  
  UIBarButtonItem *open = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:@selector(_open:)];
  UIBarButtonItem *flx1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    //分享转发按钮 —— (需要 调用 “URL” 的方法来助推)
  UIBarButtonItem *openWeb = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(_showURLInput)];
  UIBarButtonItem *flx2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  UIBarButtonItem *play = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(_play:)];
  UIBarButtonItem *flx3 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  UIBarButtonItem *loop = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(_loop:)];
  UIBarButtonItem *flx4 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  UIBarButtonItem *zoom = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(_setZoom:)];
  UIBarButtonItem *flx5 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  UIBarButtonItem *bgcolor = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:@selector(_setBGColor:)];
  UIBarButtonItem *flx6 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  UIBarButtonItem *close = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop target:self action:@selector(_close:)];
  self.toolbar.items = @[open, flx1, openWeb, flx2, loop, flx3, play, flx4, zoom, flx5, bgcolor, flx6, close];
  [self.view addSubview:self.toolbar];
  [self resetAllButtons];
  
  self.slider = [[UISlider alloc] initWithFrame:CGRectZero];
  [self.slider addTarget:self action:@selector(_sliderChanged:) forControlEvents:UIControlEventValueChanged];
  self.slider.minimumValue = 0;
  self.slider.maximumValue = 1;
  [self.view addSubview:self.slider];
}
  • 重新设置 所有按钮 的 显示效果
- (void)resetAllButtons {
  self.slider.value = 0;
  for (UIBarButtonItem *button in self.toolbar.items) {
    [self resetButton:button highlighted:NO];
  }
}
  • 设置播放按钮的 颜色 (通过bool值来判断 当前按钮的 状态并为之赋予不同的颜色)
- (void)resetButton:(UIBarButtonItem *)button highlighted:(BOOL)highlighted {
    
    //通过三目运算符来判断当前按钮的所处状态 点击“播放”后为黑色 :“其余按钮” 颜色为 “ :” 后的颜色
  button.tintColor = highlighted ? [UIColor blackColor] : [UIColor colorWithRed:50.f/255.f
                                                                        green:207.f/255.f
                                                                         blue:193.f/255.f
                                                                        alpha:1.f];
    NSLog(@"播放按钮 当前 所处 状态为 :%@",highlighted ? @"黑色" : @"绿色");
}

- (void)viewWillLayoutSubviews {
  [super viewWillLayoutSubviews];
  CGRect b = self.view.bounds;
  self.toolbar.frame = CGRectMake(0, b.size.height - 44, b.size.width, 44);
  CGSize sliderSize = [self.slider sizeThatFits:b.size];
  sliderSize.height += 12;
  self.slider.frame = CGRectMake(10, CGRectGetMinY(self.toolbar.frame) - sliderSize.height, b.size.width - 20, sliderSize.height);
  self.laAnimation.frame = CGRectMake(0, 0, b.size.width, CGRectGetMinY(self.slider.frame));
}
  • 监听到 进度条的 位置 改变时 动态赋值给当前的 进度条 以 实时改变 UI
- (void)_sliderChanged:(UISlider *)slider {
    
    
  self.laAnimation.animationProgress = slider.value;
    NSLog(@"%f",slider.value);
}


- (void)_open:(UIBarButtonItem *)button {
  [self _showJSONExplorer];
}

  • 自己定义的枚举类型方法 ——(里面是三个 不同的 视图 显示位置类型)
- (void)_setZoom:(UIBarButtonItem *)button {
  switch (self.laAnimation.contentMode) {
    case UIViewContentModeScaleAspectFit: {
      self.laAnimation.contentMode = UIViewContentModeScaleAspectFill;
      [self showMessage:@"Aspect Fill"];
    }  break;
    case UIViewContentModeScaleAspectFill:{
      self.laAnimation.contentMode = UIViewContentModeScaleToFill;
      [self showMessage:@"Scale Fill"];
    }
      break;
    case UIViewContentModeScaleToFill: {
      self.laAnimation.contentMode = UIViewContentModeScaleAspectFit;
      [self showMessage:@"Aspect Fit"];
    }
      break;
    default:
      break;
  }
}

- (void)_setBGColor:(UIBarButtonItem *)button {
  ViewBackgroundColor current = self.currentBGColor;
  current += 1;
  if (current == ViewBackgroundColorNone) {
    current = ViewBackgroundColorWhite;
  }
  self.currentBGColor = current;
}
  • 通过“URL”的方式来获取 动画 的 内容文件 并解析
- (void)_showURLInput {
  LAQRScannerViewController *qrVC = [[LAQRScannerViewController alloc] init];
    
  [qrVC setCompletionBlock:^(NSString *selectedAnimation) {
    if (selectedAnimation) {
        //调用 下面当中的 “URL” 的方法
      [self _loadAnimationFromURLString:selectedAnimation];
    }
    [self dismissViewControllerAnimated:YES completion:NULL];
  }];
    
  UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:qrVC];
    
  [self presentViewController:nav animated:YES completion:NULL];
}
  • 跳转到 列表 界面
- (void)_showJSONExplorer {
    //跳转到 列表 界面
  JSONExplorerViewController *vc = [[JSONExplorerViewController alloc] init];
    
    
    //让 该控制器“vc” 去调动 一个 block的 回传属性方法
  [vc setCompletionBlock:^(NSString *selectedAnimation) {
    if (selectedAnimation) {
        //并把 点中的 row cell的 动画名字 回传过去
      [self _loadAnimationNamed:selectedAnimation];
        NSLog(@"点中动画的名字:%@",selectedAnimation);
    }
      //执行完上边的block的方法后,自动向下弹走控制器
    [self dismissViewControllerAnimated:YES completion:NULL];
  }];
    
    //把 ”列表“ 控制器 设置为 根视图控制器
  UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:vc];
    //弹出模态ViewController(弹出控制器)
  [self presentViewController:navController animated:YES completion:NULL];
}

- (void)_loadAnimationFromURLString:(NSString *)URL {
    //跟下一个方法中的 意思 类同
  [self.laAnimation removeFromSuperview];
  self.laAnimation = nil;
  [self resetAllButtons];
  
    
    //跟下一个 方法中 不同的是 改方法中 是通过 “URL” 的路径字符串来 初始化 动画承载视图
  self.laAnimation = [[LOTAnimationView alloc] initWithContentsOfURL:[NSURL URLWithString:URL]];
  self.laAnimation.contentMode = UIViewContentModeScaleAspectFit;
  [self.view addSubview:self.laAnimation];
  [self.view setNeedsLayout];
    
}
  • 移除 “动画承载视图” 上的现有内容 并 赋值为空 (nil)
  • 不然~ 你将无法 选择 下一个 动画 展示在 视图上 ,因为 当前的 视图上已经有了 内容 并且你没有 及时 移除掉!
//上方block 中 所调用的方法
- (void)_loadAnimationNamed:(NSString *)named {
    //移除 “动画承载视图” 上的现有内容 并 赋值为空 (nil)
    //不然~ 你将无法 选择 下一个 动画 展示在 视图上 ,因为 当前的 视图上已经有了 内容 并且你没有 及时 移除掉!
  [self.laAnimation removeFromSuperview];
  self.laAnimation = nil;
    
    
    //将所有的 按钮 位置 复原 —— (尤其是 时间进度条上的button,通过复原。使其值 变为 “0”,这样滑块 自然 处于 开始的位置)
    //!!注意:如果不复原,滑块按钮 将位于 时间进度条的左右边既动画结束的位置!!
  [self resetAllButtons];
  
    
    //将上面 通过 点击某行 所传过来的 动画json文件名字 获取到 并 调用方法解析完后 满屏加载到 合适的 位置
  self.laAnimation = [LOTAnimationView animationNamed:named];
  self.laAnimation.contentMode = UIViewContentModeScaleAspectFit;
  [self.view addSubview:self.laAnimation];
    
    
    //将所需要的动画 加载到 当前视图 ——(此方法可写可不写)
  [self.view setNeedsLayout];
}

重绕方法 —— (此方法 该类中 并没有 实际调用到,所以 可写可不写。不影响操作~)

- (void)_rewind:(UIBarButtonItem *)button {
    //设置动画重载视图的 “进度”
    //实现原理是将 后期 更改的 赋值给 之前的 进度属性
  self.laAnimation.animationProgress = 0.5;
}

CADisplayLink是用于同步屏幕刷新频率的计时器,在开发中我们经常会遇到使用计时器的情况,例如图片轮播,进度条的绘制等就是比较常见的应用场景.

从runloop中移除

移除计时器有两个方法:
-- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode 和
-- (void)invalidate.

我们来分析一下他们的异同

removeFromRunLoop: forMode:会将接收者从给定的模式中移除,这个方法会对计时器进行隐式的release.
在调用removeFromRunloop方法,需要做判断,如果当期计时器不在runloop的话,会出现野指针的crash.
出现crash的原因是runloop多次调用了release方法,进行了over-release.

-- (void)invalidate是从runloop所有模式中移除计时器,并取消计时器和target的关联关系.多次调用这个方法,不会出现crash.

         

- (void)_play:(UIBarButtonItem *)button {
    // isAnimationPlaying —— 播放动画时标志为YES 为 Bool 类型
    //当检测到 该动画为 点击 “播放” 按钮后 即将进入 (动画正在播放ing) 的状态并执行下面的语句
  if (self.laAnimation.isAnimationPlaying) {
      //当处于(动画正在播放ing)的 状态时 再次点击 “播放” 按钮时 将会 暂停 "滑块" 以及 "动画" 并将 "播放" 按钮的颜色 改变~
      
      //此刷新 按钮状态的 方法 可写可不写 并没任何影响
    [self resetButton:button highlighted:NO];
      
      
    [self.laAnimation pause];
  } else {
      //CADisplayLink是用于同步屏幕刷新频率的计时器,在开发中我们经常会遇到使用计时器的情况,例如图片轮播,进度条的绘制等就是比较常见的应用场景.

    CADisplayLink *displayLink =[CADisplayLink displayLinkWithTarget:self selector:@selector(_updateSlider)] ;
      
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
      //在此处,才是真正的 改变 “播放” 按钮的 颜色 状态
    [self resetButton:button highlighted:YES];
      
    [self.laAnimation playWithCompletion:^(BOOL animationFinished) {
        //每个计时器对象只能加入到一个runloop中,但是可以被添加到不同的模式中,当CADisplayLink被加入到runloop时,会被runloop隐式retain.如果想从所有的模式中移除计时器,需要执行-invalidate()方法.
       
      [displayLink invalidate];
        
      [self resetButton:button highlighted:NO];
    }];
  }
}

- (void)_updateSlider {
    //刷新 进度条的 变化
    //将 当前 动画 的 播放进度 赋值给 进度条的 Value 值
  self.slider.value = self.laAnimation.animationProgress;
}

- (void)_loop:(UIBarButtonItem *)button {
  self.laAnimation.loopAnimation = !self.laAnimation.loopAnimation;
    // loopAnimation :告诉动画 无限循环 —— 默认为NO
    [self resetButton:button highlighted:self.laAnimation.loopAnimation];
    
    //通过判断 按钮点击的 呈现状态(重复:不重复)
    //来展示消息提示框 (Loop Enabled循环过程中" : @"Loop Disabled终止循环播放)
  [self showMessage:self.laAnimation.loopAnimation ? @"Loop Enabled循环过程中" : @"Loop Disabled终止循环播放"];
}

- (void)_close:(UIBarButtonItem *)button {
    
  [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
    
}

- (void)showMessage:(NSString *)message {
  UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  messageLabel.textAlignment = NSTextAlignmentCenter;
  messageLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
  messageLabel.textColor = [UIColor whiteColor];
  messageLabel.text = message;
  
  CGSize messageSize = [messageLabel sizeThatFits:self.view.bounds.size];
  messageSize.width += 14;
  messageSize.height += 14;
  messageLabel.frame = CGRectMake(10, 30, messageSize.width, messageSize.height);
  messageLabel.alpha = 0;
  [self.view addSubview:messageLabel];
  
  [UIView animateWithDuration:0.3 animations:^{
    messageLabel.alpha = 1;
  } completion:^(BOOL finished) {
    [UIView animateWithDuration:0.3 delay:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
      messageLabel.alpha = 0;
    } completion:^(BOOL finished) {
      [messageLabel removeFromSuperview];
    }];
  }];
}


@end
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容