iOS接入 Lottie 動畫過程詳解

Lottie 動畫簡介

  • Lottie 動畫是 airbnb 最新開源的動畫解決方案,支持多種平臺辖佣,可以使我們不費吹灰之力就可以將動畫接入 app 中,只需要一個 json 文件即可搓逾。再也不需要進(jìn)行復(fù)雜的動畫繪制工作了卷谈,節(jié)約了很多時間。下面我來詳細(xì)介紹一下 Lottie 在 iOS 端的接入過程以及部分源碼解讀霞篡。
  • 部分效果如下:
    luanchpage.gif

接入過程

首先由 CocoaPods 接入該庫

  • $ pod search Lottie
    $ pod 'lottie-ios', '~> 2.5.0'
    然后創(chuàng)建 pod 文件 $ pod init世蔗,然后將 lottie-ios 添加到 Podfile 中
    最后執(zhí)行 $ pod install 就完成了 Lottie 庫的接入
    

導(dǎo)出 json 文件

  • 我們可以在 ae 中下載插件,將 aep 文件導(dǎo)出為 json 形式朗兵,具體如何操作請看 Lottie開源動畫庫介紹

  • 導(dǎo)出的 json 文件格式化后類型如下:
    json文件格式.png
    • 其中的部分參數(shù)定義為:
      • v :版本號
      • ip:原大小
      • op:目標(biāo)大小
      • w:寬度
      • h:高度
      • nm:文件名稱
      • assets:圖片文件
      • fonts:字體
      • layers:動畫效果
      • markers:
      • chars:文字效果

將 json 和它所依賴的圖片導(dǎo)入自定義 bundle 中

  • 這里有一個問題:當(dāng)我們在做啟動導(dǎo)航圖時污淋,有時可能會需要使用到多個 page,需要多個 json 文件余掖,比如像開頭的動畫那樣寸爆,需要三個 json 文件。而 UI 導(dǎo)出的圖片基本上名字都差不多盐欺,這時候如果我們不加分辨赁豆,直接導(dǎo)入三個 json 文件,會使得圖片出現(xiàn)錯亂冗美。因為三個 json 文件中的圖片名基本完全相同魔种,這時候我們就需要
    • 打開 json 文件中的 assets 屬性,修改其中的圖片名墩衙。
    • 或者修改圖片的上層目錄务嫡,使得三個 json 文件所指向的文件為三個不同的目錄。

使用 LOTAnimationView 加載 json 文件

  • 這里使用了懶加載的方式來加載這個 view漆改, 具體過程如下,首先需要找到自定義 bundle 的地址准谚,然后再在 bundle 中找到 json 文件并且加載挫剑。
animationView.png

播放動畫

  • 在 Lottie 的 API 中我們可以看到,只需要簡單的 [firstAnimationView play] 即可播放動畫柱衔。到這里簡單的需求基本完成樊破,有時我們還需要在動畫上添加文字和按鈕來讓這個 page 能夠響應(yīng)一些事件愉棱,這時候我們就需要先將這個 LOTAniamtionView 放到視圖層級的最底層,使用 sendSubviewToBack: 這個方法即可簡單做到哲戚。然后再添加一個 backgroundView 作為容器來盛放 Label奔滑,Button 等,當(dāng)要求文字與按鈕也和動畫一樣有漸入效果時顺少,我們就可以在滑動到這個 page 時做一個簡單的動畫朋其,使用下面這個方法即可做到

    -(void)animatedForPageOne {
        [self.firstAnimationView playToProgress:1 withCompletion:nil];
        [UIView animateWithDuration:1.5 animations:^{
            self.p1bottomView.alpha = 1;
        }];
    }
    
  • 當(dāng)用戶如果滑動到第三個 page 后并沒有進(jìn)入應(yīng)用,而是滑動返回到第二個 page 時脆炎,例如文章開始時給出的動圖梅猿,這時我們就需要當(dāng)動畫播放完后,再返回動畫的第一幀秒裕,否則就會有一個跳幀的效果袱蚓,非常不好看,具體的方法也很簡單几蜻,只需要在監(jiān)聽到 ScrollView 滾動翻頁時設(shè)置 self.firstAnimationView.animationProgress = 0; 即可將上一個 page 的動畫調(diào)到第一幀喇潘,當(dāng)執(zhí)行返回時即可從第一幀開始重新播放。

其他效果

  • 當(dāng)我們在 LOTAniamtionView 上添加其他控件時梭稚,會發(fā)現(xiàn)有時候會出現(xiàn)與動畫不兼容的現(xiàn)象响蓉,比如遮擋等,這時候我們可以選擇修改控件的位置或者直接將動畫的某一部分刪除哨毁,這個過程也非常簡單枫甲,我們只需要找到 json 文件,找到相應(yīng)的控件扼褪,把它刪除即可想幻。

部分源碼分析

Lottie是如何解析json文件的?

    1. 從 LOTAniamtionView 的源文件中找到了答案话浇,首先我們可以看到在它的頭文件中找到很多初始化方法
    @interface LOTAnimationView : LOTView
    
    // 默認(rèn)從 main bundle 中加載 json 文件和圖片
    + (nonnull instancetype)animationNamed:(nonnull NSString *)animationName NS_SWIFT_NAME(init(name:));
    
    // 從給定的 bundle 中加載 json 文件和圖片脏毯,這個 animationName 參數(shù)實際上就是 json 文件的文件名
    + (nonnull instancetype)animationNamed:(nonnull NSString *)animationName inBundle:(nonnull NSBundle *)bundle NS_SWIFT_NAME(init(name:bundle:));
    
    // 直接從給定的 json 文件中加載動畫,默認(rèn)從 mainBundle 中加載
    + (nonnull instancetype)animationFromJSON:(nonnull NSDictionary *)animationJSON NS_SWIFT_NAME(init(json:));
    
    // 從一個文件 url 中加載動畫幔崖,但是不能夠使用 web url 來作為參數(shù)
    + (nonnull instancetype)animationWithFilePath:(nonnull NSString *)filePath NS_SWIFT_NAME(init(filePath:));
    
    // 給定一個反序列化的 json 文件和一個特定的 bundle 名來初始化動畫
    + (nonnull instancetype)animationFromJSON:(nullable NSDictionary *)animationJSON inBundle:(nullable NSBundle *)bundle NS_SWIFT_NAME(init(json:bundle:));
    
    /// 直接使用 LOTCompostion 來創(chuàng)建動畫效果
    - (nonnull instancetype)initWithModel:(nullable LOTComposition *)model inBundle:(nullable NSBundle *)bundle;
    
    /// 異步的從指定的 url 中加載動畫食店,這個 url 為 webUrl
    - (nonnull instancetype)initWithContentsOfURL:(nonnull NSURL *)url;
    

    至于最后是如何初始化的,我們可以根據(jù)初始化方法在 LOTComposition 類中找到??這個方法赏寇,這個方法用來解析 json 文件

    - (void)_mapFromJSON:(NSDictionary *)jsonDictionary
         withAssetBundle:(NSBundle *)bundle {
      NSNumber *width = jsonDictionary[@"w"]; // 寬度
      NSNumber *height = jsonDictionary[@"h"]; // 高度
      if (width && height) {
        CGRect bounds = CGRectMake(0, 0, width.floatValue, height.floatValue);
        _compBounds = bounds;
      }
      
      _startFrame = [jsonDictionary[@"ip"] copy]; // 初始 frame
      _endFrame = [jsonDictionary[@"op"] copy]; // 動畫結(jié)束 frame
      _framerate = [jsonDictionary[@"fr"] copy];  // 動畫變化率
      
       // 根據(jù)動畫的這三個變量來判斷動畫需要執(zhí)行的時間
      if (_startFrame && _endFrame && _framerate) {
        NSInteger frameDuration = (_endFrame.integerValue - _startFrame.integerValue) - 1;
        NSTimeInterval timeDuration = frameDuration / _framerate.floatValue;
        _timeDuration = timeDuration;  
      }
      
         
       // 圖片文件數(shù)組
      NSArray *assetArray = jsonDictionary[@"assets"];
      if (assetArray.count) {
        _assetGroup = [[LOTAssetGroup alloc] initWithJSON:assetArray withAssetBundle:bundle withFramerate:_framerate];
      }
      
       
      // 動畫layer層數(shù)組
      NSArray *layersJSON = jsonDictionary[@"layers"];
      if (layersJSON) {
        _layerGroup = [[LOTLayerGroup alloc] initWithLayerJSON:layersJSON
                                                withAssetGroup:_assetGroup
                                                 withFramerate:_framerate];
      }
      
      [_assetGroup finalizeInitializationWithFramerate:_framerate];
    }
    

    在這個方法中吉嫩,我們注意到了它調(diào)用了圖片數(shù)組的初始化方法和 layer 層數(shù)組的初始化方法,具體來看一下:

    // LOTAssetGroup 初始化方法到最后我們在 LOTAsset 中找到了這個解析 json 數(shù)據(jù)的方法
    - (void)_mapFromJSON:(NSDictionary *)jsonDictionary
          withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
           withFramerate:(NSNumber *)framerate {
      _referenceID = [jsonDictionary[@"id"] copy];  // 指定圖片的 referenceID,這個 id 是 json 文件自動為每張圖片編號的嗅定,表示他的唯一標(biāo)識符
     
      if (jsonDictionary[@"w"]) {  // 圖片寬度
        _assetWidth = [jsonDictionary[@"w"] copy];
      }
      
      if (jsonDictionary[@"h"]) { // 圖片高度
        _assetHeight = [jsonDictionary[@"h"] copy];
      }
      
      if (jsonDictionary[@"u"]) {   // 圖片的路徑自娩,這個路徑表示儲存圖片的文件夾
        _imageDirectory = [jsonDictionary[@"u"] copy];
      }
      
      if (jsonDictionary[@"p"]) {   // p 表示圖片的真實id,而不是 referenceID
        _imageName = [jsonDictionary[@"p"] copy];
      }
    
      NSArray *layersJSON = jsonDictionary[@"layers"]; // 對圖片 layer 的配置渠退,具體來看一下
      if (layersJSON) {
        _layerGroup = [[LOTLayerGroup alloc] initWithLayerJSON:layersJSON
                                                withAssetGroup:assetGroup
                                                 withFramerate:framerate];
      }
               
    

    LOTALayerGroup 文件中對 json 文件的解析

    - (void)_mapFromJSON:(NSArray *)layersJSON
          withAssetGroup:(LOTAssetGroup * _Nullable)assetGroup
           withFramerate:(NSNumber *)framerate {
      
      NSMutableArray *layers = [NSMutableArray array];
      NSMutableDictionary *modelMap = [NSMutableDictionary dictionary];
      NSMutableDictionary *referenceMap = [NSMutableDictionary dictionary];
      
      for (NSDictionary *layerJSON in layersJSON) {
        LOTLayer *layer = [[LOTLayer alloc] initWithJSON:layerJSON
                                          withAssetGroup:assetGroup
                                           withFramerate:framerate];
        [layers addObject:layer];
        modelMap[layer.layerID] = layer;
        if (layer.referenceID) {
          referenceMap[layer.referenceID] = layer;
        }
      }
      
      _referenceIDMap = referenceMap;
      _modelMap = modelMap;
      _layers = layers;
    }
    
    // 在這個方法內(nèi)部更深層的調(diào)用了 LOTALayer 的初始化方法
    - (void)_mapFromJSON:(NSDictionary *)jsonDictionary
          withAssetGroup:(LOTAssetGroup *)assetGroup
           withFramerate:(NSNumber *)framerate {
    
      _layerName = [jsonDictionary[@"nm"] copy];  // layer 的名字
      _layerID = [jsonDictionary[@"ind"] copy];  // layer 的 id忙迁,表示是第幾個 layer
      
      NSNumber *layerType = jsonDictionary[@"ty"];  // 表示 layer 的類型脐彩,這個變量是一個枚舉類型
            //   typedef enum : NSInteger {
              //      LOTLayerTypePrecomp, 
          //      LOTLayerTypeSolid,
              //      LOTLayerTypeImage,
              //      LOTLayerTypeNull,
              //      LOTLayerTypeShape,
              //      LOTLayerTypeUnknown
          //  } LOTLayerType;
               
      _layerType = layerType.integerValue;
      
      if (jsonDictionary[@"refId"]) {  // 這里的 refId 和圖片文件的 referenceID 指向的是同一個標(biāo)識符,表示這個 layer 動畫會作用在 referenceID 指向的圖片上
        _referenceID = [jsonDictionary[@"refId"] copy];
      }
      
      _parentID = [jsonDictionary[@"parent"] copy]; // 父layer
      
      if (jsonDictionary[@"st"]) { 
        _startFrame = [jsonDictionary[@"st"] copy];  // 開始的 frame
      }
      _inFrame = [jsonDictionary[@"ip"] copy];  // 開始的 frame姊扔,通常和 startFrame 值相同
      _outFrame = [jsonDictionary[@"op"] copy];  // 最后一幀的 frame
    
      if (jsonDictionary[@"sr"]) { // 縮放
        _timeStretch = [jsonDictionary[@"sr"] copy];
      } else {
        _timeStretch = @1;
      }
    
               // 判斷 layer 是哪種類型惠奸,并且做相應(yīng)的處理
      if (_layerType == LOTLayerTypePrecomp) {
        _layerHeight = [jsonDictionary[@"h"] copy];  // 高度
        _layerWidth = [jsonDictionary[@"w"] copy];  // 寬度
        [assetGroup buildAssetNamed:_referenceID withFramerate:framerate];
      } else if (_layerType == LOTLayerTypeImage) {
        [assetGroup buildAssetNamed:_referenceID withFramerate:framerate];
        _imageAsset = [assetGroup assetModelForID:_referenceID];
        _layerWidth = [_imageAsset.assetWidth copy];
        _layerHeight = [_imageAsset.assetHeight copy];
      } else if (_layerType == LOTLayerTypeSolid) {
        _layerWidth = jsonDictionary[@"sw"];
        _layerHeight = jsonDictionary[@"sh"];
        NSString *solidColor = jsonDictionary[@"sc"];
        _solidColor = [UIColor LOT_colorWithHexString:solidColor];
      }
      
      _layerBounds = CGRectMake(0, 0, _layerWidth.floatValue, _layerHeight.floatValue);
      
      NSDictionary *ks = jsonDictionary[@"ks"];
      
      NSDictionary *opacity = ks[@"o"]; // 不透明度
      if (opacity) {
        _opacity = [[LOTKeyframeGroup alloc] initWithData:opacity];
        [_opacity remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
          return LOT_RemapValue(inValue, 0, 100, 0, 1);
        }];
      }
    
      NSDictionary *timeRemap = jsonDictionary[@"tm"];
      if (timeRemap) {
        _timeRemapping = [[LOTKeyframeGroup alloc] initWithData:timeRemap];
        [_timeRemapping remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
          return inValue * framerate.doubleValue;
        }];
      }
      
      NSDictionary *rotation = ks[@"r"]; // 旋轉(zhuǎn)
      if (rotation == nil) {
        rotation = ks[@"rz"];
      }
      if (rotation) {
        _rotation = [[LOTKeyframeGroup alloc] initWithData:rotation];
        [_rotation remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
          return LOT_DegreesToRadians(inValue);
        }];
      }
      
      NSDictionary *position = ks[@"p"]; // 位置
      if ([position[@"s"] boolValue]) {
        // Separate dimensions
        _positionX = [[LOTKeyframeGroup alloc] initWithData:position[@"x"]];
        _positionY = [[LOTKeyframeGroup alloc] initWithData:position[@"y"]];
      } else {
        _position = [[LOTKeyframeGroup alloc] initWithData:position ];
      }
      
      NSDictionary *anchor = ks[@"a"]; //錨點
      if (anchor) {
        _anchor = [[LOTKeyframeGroup alloc] initWithData:anchor];
      }
      
      NSDictionary *scale = ks[@"s"];  // 縮放比例
      if (scale) {
        _scale = [[LOTKeyframeGroup alloc] initWithData:scale];
        [_scale remapKeyframesWithBlock:^CGFloat(CGFloat inValue) {
          return LOT_RemapValue(inValue, -100, 100, -1, 1);
        }];
      }
      
      _matteType = [jsonDictionary[@"tt"] integerValue];
      
      
      NSMutableArray *masks = [NSMutableArray array];
      for (NSDictionary *maskJSON in jsonDictionary[@"masksProperties"]) {
        LOTMask *mask = [[LOTMask alloc] initWithJSON:maskJSON];
        [masks addObject:mask];
      }
      _masks = masks.count ? masks : nil;
      
      NSMutableArray *shapes = [NSMutableArray array];
      for (NSDictionary *shapeJSON in jsonDictionary[@"shapes"]) { // 尺寸
        id shapeItem = [LOTShapeGroup shapeItemWithJSON:shapeJSON];
        if (shapeItem) {
          [shapes addObject:shapeItem];
        }
      }
      _shapes = shapes;
        
               // 額外效果
      NSArray *effects = jsonDictionary[@"ef"];
      if (effects.count > 0) {
        
        NSDictionary *effectNames = @{ @0: @"slider",
                                       @1: @"angle",
                                       @2: @"color",
                                       @3: @"point",
                                       @4: @"checkbox",
                                       @5: @"group",
                                       @6: @"noValue",
                                       @7: @"dropDown",
                                       @9: @"customValue",
                                       @10: @"layerIndex",
                                       @20: @"tint",
                                       @21: @"fill" };
                                 
        for (NSDictionary *effect in effects) {
          NSNumber *typeNumber = effect[@"ty"];
          NSString *name = effect[@"nm"];
          NSString *internalName = effect[@"mn"];
          NSString *typeString = effectNames[typeNumber];
          if (typeString) {
            NSLog(@"%s: Warning: %@ effect not supported: %@ / %@", __PRETTY_FUNCTION__, typeString, internalName, name);
          }
        }
      }
    }
    

    由上可以看到,在 LOTComposition , LOTLayer, LOTAssets 這幾個類中完成了 json 數(shù)據(jù)的解析恰梢。

LOTAniamtionView 的使用方法

  • 首先我們可以通過上述方法初始化一個 animationView佛南, 然后繼續(xù)觀察 API 中的屬性和方法:

    /// 判斷是否正在播放動畫
    @property (nonatomic, readonly) BOOL isAnimationPlaying;
    
    /// 判斷動畫是否需要循環(huán)播放
    @property (nonatomic, assign) BOOL loopAnimation;
    
    /// 如果這個屬性值和 loopAnimation 都為 YES, 那么動畫會先倒序播放,然后再正序播放
    @property (nonatomic, assign) BOOL autoReverseAnimation;
    
    // 設(shè)置 progress 的值 從 0 ~ 1删豺,表示這個動畫執(zhí)行的程度共虑,當(dāng)設(shè)定為 1 時表示動畫結(jié)束
    @property (nonatomic, assign) CGFloat animationProgress;
    
    // 為動畫設(shè)置一個播放速度,如果想倒序播放呀页,則可以把這個值設(shè)定為負(fù)數(shù)
    @property (nonatomic, assign) CGFloat animationSpeed;
    
    // 只讀屬性妈拌,獲取的值為當(dāng) speed = 1 時動畫的播放秒數(shù)
    @property (nonatomic, readonly) CGFloat animationDuration;
    
    // 是否緩存動畫, 默認(rèn)為 YES, 對于只需要播放一次的動畫蓬蝶,比如啟動頁動畫尘分,可以設(shè)定為 NO
    @property (nonatomic, assign) BOOL cacheEnable;
    
    /// 當(dāng)動畫播放完畢所調(diào)用的 block
    @property (nonatomic, copy, nullable) LOTAnimationCompletionBlock completionBlock;
    
    /// 直接設(shè)定播放數(shù)據(jù),也就是解析 json 文件后的數(shù)據(jù)模型
    @property (nonatomic, strong, nullable) LOTComposition *sceneModel;
    

    在上述屬性中我們可以看到一個緩存 cache 屬性丸氛,等一下我們將會一起來探究 Lottie 究竟是如何緩存動畫的培愁,緊接著我們來接著看 LOTAniamtionView 的 API 中所定義的部分方法:

    /*
     * 這個方法表示 animation 將從當(dāng)前的 position 播放到指定的 progress
     * 當(dāng) loopAnimation 屬性為 YES 時,這個動畫將從當(dāng)前 position 到指定 progress 并且無限循環(huán)
     * 當(dāng) loopAnimation 屬性為 YES 時缓窜,這個動畫將從當(dāng)前 position 到指定 progress 然后停到 progress 的那一幀
     * 當(dāng)動畫完成后會調(diào)用指定 block
     */
    - (void)playToProgress:(CGFloat)toProgress
            withCompletion:(nullable LOTAnimationCompletionBlock)completion;
    
    /*
     * 和上面的方法差不多定续,主要是開始的 progress 也可以由我們來指定
     */
    - (void)playFromProgress:(CGFloat)fromStartProgress
                  toProgress:(CGFloat)toEndProgress
              withCompletion:(nullable LOTAnimationCompletionBlock)completion;
    
    /*
     * 從當(dāng)前的 position 動畫到指定的 frame
     */
    - (void)playToFrame:(nonnull NSNumber *)toFrame
         withCompletion:(nullable LOTAnimationCompletionBlock)completion;
    
    /*
     * 設(shè)置開始的 frame 并且動畫到指定的 frame
     */
    - (void)playFromFrame:(nonnull NSNumber *)fromStartFrame
                  toFrame:(nonnull NSNumber *)toEndFrame
           withCompletion:(nullable LOTAnimationCompletionBlock)completion;
    
    
    /**
     * 從當(dāng)前的 position 完成到結(jié)束 position
     **/
    - (void)playWithCompletion:(nullable LOTAnimationCompletionBlock)completion;
    
    /// 簡單的播放到動畫結(jié)尾,播放完成后會調(diào)用完成block
    - (void)play;
    
    /// 暫停動畫并且調(diào)用完成block
    - (void)pause;
    
    /// 停止當(dāng)前動畫并且倒回到最開始的那一幀禾锤,完成后調(diào)用block
    - (void)stop;
    
    /// 設(shè)置當(dāng)前的動畫到指定的 frame私股,如果當(dāng)前動畫正在播放則會停止動畫并且調(diào)用完成block
    - (void)setProgressWithFrame:(nonnull NSNumber *)currentFrame;
    
    /// 強制對現(xiàn)在的 frame 進(jìn)行升級
    - (void)forceDrawingUpdate;
    
    /// 打印所有繼承鏈上的 keypath
    - (void)logHierarchyKeypaths;
    

Lottie 是如何緩存動畫的

  • 在 LOTAniamtionCache 的實現(xiàn)文件中可以看到以下方法:

    // 表示是否支持緩存
    - (void)setCacheEnable:(BOOL)cacheEnable {
      _cacheEnable = cacheEnable;
      if (!self.sceneModel.cacheKey) {
        return;
      }
      if (cacheEnable) {
    //      如果支持,則向 cache 中添加這個key所代表的對象已經(jīng)向字典中添加這個 key 以及它對應(yīng)的 value 值恩掷,也就是動畫數(shù)據(jù)對象
        [[LOTAnimationCache sharedCache] addAnimation:_sceneModel forKey:self.sceneModel.cacheKey];
      } else {
    //      如果不支持倡鲸,則從字典中移除這個 key,和這個 key 所代表的對象黄娘,以及數(shù)組中的 key
        [[LOTAnimationCache sharedCache] removeAnimationForKey:self.sceneModel.cacheKey];
      }
    }
    
    // 具體方法如下:
    // 可以看到峭状,LOTAnimationCache 維護(hù)了一個添加 key 和 value 到字典和一個數(shù)組
    @implementation LOTAnimationCache {
      NSMutableDictionary *animationsCache_;
      NSMutableArray *lruOrderArray_;
    }
    
    // 當(dāng)添加動畫時:首先需要判斷當(dāng)前數(shù)組的 size 是否已經(jīng)大于了最大的 size,如果是的話逼争,則先清除最前面緩存的動畫优床,然后再添加新的動畫,而這個 const NSInteger kLOTCacheSize = 50;最大值為 50
    - (void)addAnimation:(LOTComposition *)animation forKey:(NSString *)key {
      if (lruOrderArray_.count >= kLOTCacheSize) {
        NSString *oldKey = lruOrderArray_[0];
        [animationsCache_ removeObjectForKey:oldKey];
        [lruOrderArray_ removeObject:oldKey];
      }
      [lruOrderArray_ removeObject:key];
      [lruOrderArray_ addObject:key];
      [animationsCache_ setObject:animation forKey:key];
    }
    
    // 當(dāng)移除動畫時:則直接將緩存有該動畫的數(shù)組中移除這個 key氮凝,并且在 cache 字典中也移除這個 key 和它所對應(yīng)的對象羔巢。
    - (void)removeAnimationForKey:(NSString *)key {
      [lruOrderArray_ removeObject:key];
      [animationsCache_ removeObjectForKey:key];
    }
    

    接下來在閱讀 LOTAniamtionCache 的實現(xiàn)文件,可以發(fā)現(xiàn)罩阵,整個 cache 是一個單例竿秆,也就是存在于 app 的整個生命周期中不會被銷毀,一旦 app 關(guān)閉稿壁,由于數(shù)據(jù)存儲也僅僅是簡單的使用一個數(shù)組和一個字典來存儲幽钢,并未進(jìn)行持久化處理,單例中所緩存的數(shù)據(jù)也會被銷毀傅是,所以我們對于動畫的緩存僅限于我們在使用 app 時匪燕。

    + (instancetype)sharedCache {
      static LOTAnimationCache *sharedCache = nil;
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        sharedCache = [[self alloc] init];
      });
      return sharedCache;
    }
    
    - (instancetype)init {
      self = [super init];
      if (self) {
        animationsCache_ = [[NSMutableDictionary alloc] init];
        lruOrderArray_ = [[NSMutableArray alloc] init];
      }
      return self;
    }
    

總結(jié)

  • 此次僅僅是從表面上簡單的分析了 Lottie 這個庫是如何解析 json 文件,以及其常用的幾個方法和如何進(jìn)行動畫緩存的喧笔,以后如果有時間的話帽驯,會繼續(xù)學(xué)習(xí)這個庫的實現(xiàn)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末书闸,一起剝皮案震驚了整個濱河市尼变,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浆劲,老刑警劉巖嫌术,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牌借,居然都是意外死亡度气,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門膨报,熙熙樓的掌柜王于貴愁眉苦臉地迎上來磷籍,“玉大人,你說我怎么就攤上這事现柠≡毫欤” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵晒旅,是天一觀的道長栅盲。 經(jīng)常有香客問我,道長废恋,這世上最難降的妖魔是什么谈秫? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮鱼鼓,結(jié)果婚禮上拟烫,老公的妹妹穿的比我還像新娘。我一直安慰自己迄本,他們只是感情好硕淑,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般置媳。 火紅的嫁衣襯著肌膚如雪于樟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天拇囊,我揣著相機與錄音迂曲,去河邊找鬼。 笑死寥袭,一個胖子當(dāng)著我的面吹牛路捧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播传黄,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼杰扫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膘掰?” 一聲冷哼從身側(cè)響起章姓,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炭序,沒想到半個月后啤覆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡惭聂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年窗声,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜纲。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡笨觅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耕腾,到底是詐尸還是另有隱情见剩,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布扫俺,位于F島的核電站苍苞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狼纬。R本人自食惡果不足惜羹呵,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疗琉。 院中可真熱鬧冈欢,春花似錦、人聲如沸盈简。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至香浩,卻和暖如春类缤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弃衍。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工呀非, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坚俗,地道東北人镜盯。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像猖败,于是被迫代替她去往敵國和親速缆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容