1. 程式人生 > >iOS動態圖實現(1)

iOS動態圖實現(1)

動態圖的分類

  • GIF : GIF圖形交換格式是一種點陣圖圖形檔案格式,以8位色(即256種顏色)重現真彩色的影象,誕生在windows1.0的時代,已經有27年的歷史,廣泛的應用在影象的網路傳輸中。
  • WEBP : 2010年穀歌推出的圖片格式,專門用來在web中使用,只有opera和chrome支援。
  • APNG : 這東西是mozilla搞出來的,它是24位的,可以容納1680萬種顏色,也是為了取代GIF,目前有火狐和Safari支援。

各種格式的圖片大小效能比較可以在 GIF vs APNG vs WEBP 中看到,騰訊的同學也開源了一些工具可以用來轉換不同格式的動態圖,比如 iSpatra

iOS動圖的儲存

iOS 的相簿是支援儲存 GIF 和 APNG 動圖的,只是不能直接播放。用

[ALAssetsLibrary    writeImageDataToSavedPhotosAlbum:metadata:completionBlock]

可以直接把 APNG、GIF 的資料寫入相簿。如果圖省事直接用 UIImageWriteToSavedPhotosAlbum() 寫相簿,那麼影象會被強制轉碼為 PNG。

目前來說,儲存 UIImage 有三種方式:1.直接用 NSKeyedArchiver 把 UIImage 序列化儲存,2.用 UIImagePNGRepresentation()

先把圖片轉為 PNG 儲存,3.用 UIImageJPEGRepresentation() 把圖片壓縮成 JPEG 儲存。

實際上,NSKeyedArchiver 是呼叫了 UIImagePNGRepresentation 進行序列化的,用它來儲存圖片是消耗最大的。蘋果對 JPEG 有硬編碼和硬解碼,儲存成 JPEG 會大大縮減編碼解碼時間,也能減小檔案體積。所以如果圖片不包含透明畫素時,UIImageJPEGRepresentation(0.9) 是最佳的圖片儲存方式,其次是 UIImagePNGRepresentation()

GIF動態圖的播放

1.使用UIImageView來播放

//建立UIImageView,新增到介面
UIImageView *imageView = [[UIImageView alloc]   initWithFrame:CGRectMake(20, 20, 100, 100)];
[self.view addSubview:imageView];
 //建立一個數組,陣列中按順序新增要播放的圖片(圖片為靜態的圖片)
NSMutableArray *imgArray = [NSMutableArray array];
for (int i=1; i<7; i++) {
UIImage *image = [UIImage imageNamed:[NSString  stringWithFormat:@"gif%02d.png",i]];
[imgArray addObject:image];
}
//把存有UIImage的陣列賦給動畫圖片陣列
imageView.animationImages = imgArray;
//設定執行一次完整動畫的時長
imageView.animationDuration = 6*0.15;
//動畫重複次數 (0為重複播放)
imageView.animationRepeatCount = 0;
//開始播放動畫
[imageView startAnimating];
//停止播放動畫  - (void)stopAnimating;
//判斷是否正在執行動畫  - (BOOL)isAnimating;

如果圖片數量較多的話,會佔用很大的記憶體,每幀的播放時間一樣,失去了原有動態圖的播放延遲時間

2.用UIWebView來顯示動態圖

//得到圖片的路徑
 NSString *path = [[NSBundle mainBundle]    pathForResource:@"happy" ofType:@"gif"];
//將圖片轉為NSData
NSData *gifData = [NSData dataWithContentsOfFile:path];
//建立一個webView,新增到介面
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 150, 200, 200)];
[self.view addSubview:webView];
//自動調整尺寸
webView.scalesPageToFit = YES;
//禁止滾動
webView.scrollView.scrollEnabled = NO;
//設定透明效果
webView.backgroundColor = [UIColor clearColor];
webView.opaque = 0;
//載入資料
[webView loadData:gifData MIMEType:@"image/gif"     textEncodingName:nil baseURL:nil];

無法控制進度,可控性不強,Web View 沒有為在移動裝置上播放 GIF 而做優化,經常會降低播放速度。同時也不能很好地控制回放過程或記憶體佔用

3.使用自定義的view來播放動態圖

其實使用自定義的view來播放動態圖所採取的做法無非是給View加一個image屬性,所以決定採用繼承UIImageView和UIImage來實現動態圖的播放。
動態圖控制元件的優劣主要要考慮一下因素:

1.CPU使用率

2.記憶體消耗

3.滑動顯示的幀率

基於以上影響效能的考慮,決定通過一個可變陣列快取動態圖的10幀影象,然後在讀取當前幀的影象時,刪除陣列中快取的第n幀影象,非同步執行緒載入後面對應的第n+10幀影象,以此來達到減少記憶體消耗的目的,示例程式碼如下:

- (UIImage * )getFrameImageAtIndex:(NSUInteger)index
{
    UIImage* frame = nil;
    @synchronized(self.frameImages) {
        frame = self.frameImages[index];
    }
    if (!frame || [frame isKindOfClass:[NSNull class]]) {
        CGImageSourceStatus state = CGImageSourceGetStatus(_imageSource);
        CGImageRef image = CGImageSourceCreateImageAtIndex(_imageSource, index, NULL);
        if (image != NULL) {
            frame = [UIImage imageWithCGImage:image scale:imageScale orientation:UIImageOrientationUp];
            CFRelease(image);
        }
    }

    if (self.frameCount > productLimitNum) {

        [self.frameImages replaceObjectAtIndex:index withObject:[NSNull null]];
        NSUInteger nextReadIdx = (index + productLimitNum);
        nextReadIdx %= self.frameCount;
        if([self.frameImages[nextReadIdx] isKindOfClass:[NSNull class]]) {
            dispatch_async(_serialQueue, ^{
                CGImageSourceStatus state = CGImageSourceGetStatus(_imageSource);
                CGImageRef image = CGImageSourceCreateImageAtIndex(_imageSource, nextReadIdx, NULL);
                @synchronized(self.frameImages) {
                    if (image != NULL) {
                        [self.frameImages replaceObjectAtIndex:nextReadIdx withObject:[UIImage imageWithCGImage:image scale:imageScale orientation:UIImageOrientationUp]];
                    } else {
                        [self.frameImages replaceObjectAtIndex:nextReadIdx withObject:[NSNull null]];
                    }
                    CFRelease(image);
                }
            });
        }
    }
    return frame;
}

通過CADisplayLink來同步的重新整理當前UIImageView顯示的影象,重新整理的頻率根據相應的裝置而定,一般會在60fps左右,最終達到影象動態播放的目的。

- (CADisplayLink *)displayLink
{
    if (self.superview && self.animatedImage) {
        if (_displayLink) {
            return _displayLink;
        }else{
            _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeCurrentFrame:)];
            [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        }
    } else {
        [_displayLink invalidate];
        _displayLink = nil;
    }
    return _displayLink;
}

- (void)changeCurrentFrame:(CADisplayLink *)displayLink
{
    if (self.currentFrameIndex >= self.animatedImage.frameCount) {
        return;
    }
    self.accumulator += fmin(displayLink.duration, 0.1);

    while (self.accumulator >=  self.animatedImage.frameDurations[self.currentFrameIndex]) {
        self.accumulator -= self.animatedImage.frameDurations[self.currentFrameIndex];
        if (++self.currentFrameIndex >= self.animatedImage.frameCount) {
            if (--self.loopCountdown == 0) {
                [self stopAnimating];
                return;
            }
            self.currentFrameIndex = 0;
        }
        self.currentFrameIndex = MIN(self.currentFrameIndex, [self.animatedImage.images count] - 1);
        self.currentFrame = [self.animatedImage getFrameImageAtIndex:self.currentFrameIndex];
        [self.layer setNeedsDisplay];
    }
}

除了通過CADisplayLink來達成動態圖重新整理之外還有兩種方法也可以達到目的:

  • 通過設定動畫屬性,為layer添CAKeyframeAnimation

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
    NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
    CGFloat currentTime = 0;
    int count = _frameDelayTimes.count;
    for (int i = 0; i < count; ++i) {
        [times addObject:[NSNumber numberWithFloat:(currentTime/_totalTime)]];
        currentTime += [[_frameDelayTimes objectAtIndex:i] floatValue];
    }
    [animation setKeyTimes:times];
    NSMutableArray *images = [NSMutableArray arrayWithCapacity:3];
    for (int i = 0; i < count; ++i) {
        [images addObject:[_frames objectAtIndex:i]];
    }
    [animation setValues:images];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
    animation.duration = _totalTime;
    animation.delegate = self;
    animation.repeatCount = 5;
    
    [self.layer addAnimation:animation forKey:@"gifAnimation"];
    
  • 通過NSTimer遞迴呼叫,傳入每一幀的持續時間

    • scheduledTimerWithTimeInterval:(NSTimeInterval)time invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

YZImageView和YZImage的使用

#import "ViewController.h"
#import "YZImageView.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet YZImageView *test1;
@property (weak, nonatomic) IBOutlet YZImageView *test2;
@property (weak, nonatomic) IBOutlet YZImageView *test3;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.test1.animatedImage = (YZAnimationImage *)[YZAnimationImage imageNamed:@"test1"];
    self.test2.animatedImage = (YZAnimationImage *)[YZAnimationImage imageNamed:@"test2"];
    self.test3.animatedImage = (YZAnimationImage *)[YZAnimationImage imageNamed:@"[email protected]"];
}   

使用效果

相關推薦

iOS動態實現1

動態圖的分類 GIF : GIF圖形交換格式是一種點陣圖圖形檔案格式,以8位色(即256種顏色)重現真彩色的影象,誕生在windows1.0的時代,已經有27年的歷史,廣泛的應用在影象的網路傳輸中。 WEBP : 2010年穀歌推出的圖片格式,專門用來

像語義分割代碼實現1

getcwd classes ner copy imp rec snapshot ini str 谷歌最新語義圖像分割模型 DeepLab-v3+ 現已開源 https://www.oschina.net/news/94257/google-open-sources-pix

第十二週專案3 - 遍歷演算法實現1

/*Copyright (c) 2015, 煙臺大學計算機與控制工程學院 * All rights reserved. * 檔名稱:H1.cpp * 作者:辛志勐 * 完成日期:2015年11月23日 * 版本號:VC6.0 * 問題描述:實現圖遍歷演算法,輸出圖結構的深度優先(DFS)遍歷序列

微信小程式實現動態新增標籤1

說明:最近入住微信小程式,將自己所學經歷分享出來,包括學到的知識,踩到的坑,一起分享給大家,後續會慢慢更新: 首先針對一些幾乎沒有基礎的童鞋,附上一些連結,大致看完後能夠有能力製作簡單的小程式了: 另外特別推薦一波福利(樣式庫,也就是說,各種

線性表及其實現1

結構 span 1.5 logs cnblogs 一元多項式 lin wid -s 線性表及其實現(1)   多項式的表示:   【例】一元多項式及其運算   一元多項式:   主要運算:多項式相加、相減、相乘等。   【分析】如何表示多項式?   ? 多項式項數n   

在STM32上實現NTFS之4:GPT分區表的C語言實現1:主GPT表頭的實現

center mbr分區 sum 對齊 字節數 決定 容器 alt 水平 題外話:在荒廢了很久沒有更新之後……某日突然收到讀者的站內信!內容大體是詢問GPT分區表信息的讀取方式,筆者激動萬分之下,決定繼續解剖NTFS……其實GPT嚴格上不算是NTFS的內容, GPT和M

智能指針原理及實現1- shared_ptr

red ++ 直接 初始 targe -- div urn 記錄 C++沒有內存回收機制,每次程序員new出來的對象需要手動delete,流程復雜時可能會漏掉delete,導致內存泄漏。於是C++引入智能指針,可用於動態資源管理,資源即對象的管理策略。 一、智能指針類別 智

HashMap的底層原理實現1

TP CQ 鍵值對 jpeg 需要 dns cnp 第一步 進行 ———————————— 眾所周知,HashMap是一個用於存儲Key-Value鍵值對的集合,每一個鍵值對也叫做Entry。這些個鍵值對(Entry)分散存儲

c專案實現1實現電子詞典的翻譯

專案實現功能 通過使用者的輸入,在字典檔案中進行 查詢,返回對應的翻譯內容。 字典檔案的樣式,該檔案已經上傳。 #a                       

SpringBoot使用WebSocket實現服務端推送---單機實現1

最近開發中需要實現服務端的推送,經過一段時間的資料查詢最終鎖定使用websocket來實現。JavaEE本身就支援WebSocket。我們只需要開發一個EndPoint來處理連線、訊息等即可。但是WebSocket的session管理是開發中的重中之重和難點,因為你需要知道推送給誰,就需要儲存代

熟練使用Lua面向物件:基於table的面向物件實現1

轉:https://www.cnblogs.com/yao2yaoblog/p/6433553.html c++和java語言機制中本身帶有面向物件的內容,而lua設計的思想是超程式設計,沒有面向物件的實現。 但是利用lua的元表(matetable)機制,可以實現面向物件。要講清楚怎樣

R語言視覺化作筆記1

R語言ggplot2與plotly的基本介紹 ggplot2 以R包自帶的資料mpg為例 library(ggplot2) data0 <- mpg ggplot(data = data0,mapping = aes(x=displ))+ geom_density()

Linux核心設計與實現1--核心開發的特點

1. 核心程式設計時既不能訪問C庫也不能訪問標準的C標頭檔案        其中的原因有很多種。其一,C標準庫的很多函式實現都是基於核心實現的,這核心編譯的時候都還沒有核心,所以就不存在這些函式,這個就是先有雞還是先有蛋這個悖論。其二,其主主要的的

[iOS]CoreText 學習筆記1

感謝唐巧大神的文章,總結一下學習筆記. 完善的排版引擎,所有的程式碼(顏色、frame等)按照不同的功能分成不同的類。 按照以上原則,將CTDisplayView中的部分內容拆開,由 4 個類構成: CTFrameParserConfig類,用於配置繪製的引數,例如

負載均衡演算法---Java的簡單實現1

最近,大夥常在談論什麼負載均衡,什麼伺服器的,而自己對於這一塊也是不太理解深入模糊,然後就去看書學習,印證自己的想法。下面是自己的一些總結吧:   比較常用的負載均衡演算法,有下面的這一些: (1)輪詢(其實就是迴圈) (2)隨機 (3)hash (4)加權輪詢

類和動態記憶體分配1

假設我們要建立一個類,其中有一個成員表示某人的姓,最簡單的就是用字串陣列來儲存,開始使用14個字元的陣列,發現太小,保險的方法是使用40個字元的陣列,但是當建立2000多個這個樣的物件時,必定會造成記憶體浪費。通常使用string類,該類有良好的記憶體管理細節。但是這樣就沒有機會深入的學習記

JVM垃圾回收- GC算法:實現1

並行 ctime 配置 使用情況 ava 第一個 中標 算法 bsp GC算法:實現 上面我們介紹了GC算法中的核心概念,接下來我們看一下JVM裏的具體實現。首先必須了解的一個重要的事實是:對於大部分的JVM來說,兩種不同的GC算法是必須的,一個是清理Young Gene

sublime中基礎的程式碼實現1

一、計算1——10相加的結果 count = 1 sum = 0 while count <=10: sum += count print(‘第%d次執行,此時sum=%d,count = %d’,%(count,sum,count)) count +=

異端審判器!一個泛用型文字聚類模型的實現1

給你的入侵檢測系統提供一個靈感。 如果給你一大堆使用者輸入,裡面有大量的中文地名,像是“北京”、“成都”、“東莞”,不幸的是,其中也混有一些羅馬地名,比如 “Singapore”、“New York”、“Tokyo”。你的任務是將它們分開,你會如何去做? 當然,有

JavaScript精華筆記:ES5陣列新增函式的原始碼實現1

本篇文章中,對forEach、filter、map、Every、Some、reduce和reduceRight等函式,講述瞭如何自己編寫程式碼實現它們的功能。 通過閱讀原始碼,自己編寫原始碼,能瞭解編寫思想、熟悉設計模式,能鍛鍊自己編寫元件、框架的能力。 試驗物件 所有的函式原始