UIImageView 序列幀動畫的實現(xiàn)以及內(nèi)存的優(yōu)化

UIImageView 序列幀動畫的實現(xiàn)以及內(nèi)存的優(yōu)化

最近筆者項目中又一次遇到了UIImageView的序列幀動畫,各種百度,為了不讓下一次遇到序列幀動畫的時候還需要百度,也為了讓后來查資料的人有一個系統(tǒng)的理解,我準(zhǔn)備將這些百度來的資料以及自己的理解寫成一個demo供大家參考學(xué)習(xí);


有夢就有希望

UIImageView動圖三種實現(xiàn)方式:

實現(xiàn)方式一: SDWebImage實現(xiàn)

接到需求最初的想法就是用第三方來實現(xiàn)播放動畫gif動畫了;用SDWebImage來實現(xiàn)gif的播放代碼如下:

- (void)achieveGifWithSDWebImage {
    NSString  *filePath = [[NSBundle bundleWithPath:[[NSBundle mainBundle] bundlePath]] pathForResource:@"icon_image.gif" ofType:nil];
    NSData  *imageData = [NSData dataWithContentsOfFile:filePath];
    self.imageView.image = [UIImage sd_animatedGIFWithData:imageData];
}
內(nèi)存占用情況

沒有加載動畫之前:
WX20180820-123210.png

加載動畫中:
2.png

退出動畫之后:
3.png

這種實現(xiàn)方式省心省力,只需要自己手動加載一下資源就好,內(nèi)存不用自己手動控制,就可以釋放;

實現(xiàn)方式二:UIImageView實現(xiàn)

其實UIImageView有一套自己播放gif圖的方式的,不過其播放的不是gif格式的動圖,而是一幀幀的進行播放的:

- (void)createAnimation {
    // 1.將序列圖加入數(shù)組
    NSMutableArray *imagesArray = [[NSMutableArray alloc] init];
    for (NSInteger i = 1;i <= 26;i++)
    {
//        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"icon_image_00%02ld.png",I]];
//        [imagesArray addObject:image];
        // 計算文件名
        NSString *filename = [NSString stringWithFormat:@"icon_image_00%02ld.png",I];
        NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
        UIImage *image = [UIImage imageWithContentsOfFile:path];
        [imagesArray addObject:image];
    }
    // 設(shè)置序列圖數(shù)組
    self.imageView.animationImages = imagesArray;
    // 設(shè)置播放周期時間
    self.imageView.animationDuration = 0.1*imagesArray.count;
    // 設(shè)置播放次數(shù)
    self.imageView.animationRepeatCount = 0;
    // 播放動畫
    [self.imageView startAnimating];
}

注意:這里加載圖片資源沒有用到UIImage中的imageNamed:方法,而是用了imageWithContentsOfFile方法,有什么樣的區(qū)別呢?本文末尾有關(guān)于此問題的解答。

內(nèi)存占用情況

此種方式加載動畫的內(nèi)存占用情況與sd加載gif效果一樣,只是需要手動做一步釋放,否則占用內(nèi)存不能及時釋放:

    // 停止動畫
    [self.imageView stopAnimating];
    // 將圖片資源進行釋放
    self.imageView.animationImages = nil;
    // 加載到內(nèi)存中的資源要進行釋放  最好用_imagesArray方式,self.imagesArray方式有的時候釋放不掉;
    _imagesArray = nil;

這兩個種實現(xiàn)方式有一個共同的特點,就是動畫結(jié)束后沒有一個明確的回調(diào),如果是一個動畫結(jié)束之后另外一個動畫緊接著開始了,只靠定時器來完成總是會出現(xiàn)這種或者那種問題,要么是動畫錯位,要么是因為網(wǎng)絡(luò)問題播放不流暢導(dǎo)致出各種不可預(yù)知的問題,這個時候,有一個能準(zhǔn)確檢測到動畫結(jié)束的回調(diào)對我們來說是至關(guān)重要的,那么下面我們來看看如何進行實現(xiàn)?應(yīng)該以何種方式進行實現(xiàn)呢?

能準(zhǔn)確監(jiān)視動畫結(jié)束事件的實現(xiàn)方式----UIImageview的關(guān)鍵幀動畫CAKeyframeAnimation
 //存放圖片的數(shù)組
    NSMutableArray *array = [NSMutableArray array];
    for(NSUInteger i = 1;i < 26 ;i++) {
//        UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"icon_image_00%02ld",i]];
        // 計算文件名
        NSString *filename = [NSString stringWithFormat:@"icon_image_00%02ld.png",I];
        NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
        UIImage *image = [UIImage imageWithContentsOfFile:path];
        CGImageRef cgimg = image.CGImage;
        [array addObject:(__bridge UIImage *)cgimg];
    }
    _imagesArray = array;
    // 創(chuàng)建CAKeyframeAnimation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
    // 動畫結(jié)束之后的回調(diào)
    animation.delegate = self;
    animation.duration = array.count * 0.1;
    animation.repeatCount = 2;
    // 設(shè)置animation的唯一標(biāo)示,這樣在delegate回調(diào)的時候能夠區(qū)分開來
    [animation setValue:@"animation1" forKey:@"customType"];
    animation.values = array;
    [self.imageView.layer addAnimation:animation forKey:@""];

在動畫結(jié)束的時候想釋放資源內(nèi)存,只需要這樣就可以了:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    // 動畫結(jié)束回調(diào)
    NSString *keyPathValue = [anim valueForKey:@"customType"];
    if ([keyPathValue isEqualToString:@"animation1"]) {
        NSLog(@"這個動畫結(jié)束了");
        _imagesArray = nil;
    }
}

兩種圖片加載方式的區(qū)別:

+(UIImage *)imageNamed:(NSString *)name
+(UIImage *)imageWithContentsOfFile:(NSString *)name

查閱官方文檔可以總結(jié)如下:

imageNamed:在加載圖片時會根據(jù)名字在主目錄中查找,首先會在內(nèi)存緩存中查找,如果沒有再從磁盤緩存中獲取,之后系統(tǒng)會緩存該圖片到內(nèi)存中。
imageWithContentsOfFile:僅加載圖片,圖像數(shù)據(jù)不會緩存。

因此我們可以得到這樣的結(jié)論:

  1. 當(dāng)我們頻繁的使用一個圖片的時候(例如:在一個tableview的cell中會加載一個圖標(biāo),那么用imageNamed:方法效率會很高)使用imageNamed方法;
  2. 然而iOS的內(nèi)存非常珍貴并且在內(nèi)存消耗過大時,會強制釋放內(nèi)存,即會遇到memory warnings。而在iOS系統(tǒng)里面釋放圖像的內(nèi)存是一件比較麻煩的事情,有可能會造成內(nèi)存泄漏。例如:當(dāng)一個UIView對象的animationImages是一個裝有UIImage對象動態(tài)數(shù)組NSMutableArray,并進行逐幀動畫。當(dāng)使用imageNamed的方式加載圖像到一個動態(tài)數(shù)組NSMutableArray,這將會很有可能造成內(nèi)存泄露。原因很顯然的。
  3. 加載比較大的圖片的時候切記使用imageNamed:,應(yīng)該使用imageWithContentsOfFile:;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容