溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS的GIF動畫效果實現(xiàn)

發(fā)布時間:2020-07-09 02:02:20 來源:網(wǎng)絡(luò) 閱讀:715 作者:博文視點 欄目:移動開發(fā)

引言:GIF圖像格式是常見的一種動態(tài)圖片格式,無論是在Web端還是在移動端都經(jīng)常遇到,但是考慮目前iOS還無法原生展現(xiàn)GIF圖片,而對于GIF的原生支持暫時也沒有像JPG、PNG等圖像格式支持得這么全面,因此本文從圖片的合成與分解角度來為大家講解GIF的知識,結(jié)合ImageIO框架可以更方便地實現(xiàn)GIF圖片的合成與分解。 
本文選自《iOS動畫——核心技術(shù)與案例實戰(zhàn)》。

GIF在iOS中的使用場景

  GIF在iOS中的使用場景有以下三個方面。 
(1)GIF圖片分解為單幀圖片。 
(2)一系列單幀圖片合成GIF圖片。 
(3)iOS系統(tǒng)上展示GIF動畫效果。 
  在GIF的合成和分解方面將會接觸到iOS圖像處理核心框架ImageIO,作為iOS系統(tǒng)中圖像處理的核心框架,它為我們提供了各種豐富的API,本文將要實現(xiàn)的GIF分解與合成功能,通過ImageIO就可以很方便地實現(xiàn)。GIF動畫展示效果將結(jié)合UIImageView和定時器,利用逐幀展示的方式為大家呈現(xiàn)GIF動畫效果。

GIF分解單幀圖片

1 GIF圖片分解過程

  GIF分解為單幀圖片的過程如下。 
 iOS的GIF動畫效果實現(xiàn)
  整個過程劃分為5個模塊、4個過程,分別如下。 
(1)本地讀取GIF圖片,將其轉(zhuǎn)換為NSdata數(shù)據(jù)類型。 
(2)將NSData作為ImageIO模塊的輸入。 
(3)獲取ImageIO的輸出數(shù)據(jù):UIImage。 
(4)將獲取到的UIImage數(shù)據(jù)存儲為JPG或者PNG格式保存到本地。 
  在整個GIF圖片分解的過程中,ImageIO是處理過程的核心部分。它負(fù)責(zé)對GIF文件格式進(jìn)行解析,并將解析之后的數(shù)據(jù)轉(zhuǎn)換為一幀幀圖片輸出。幸運的是我們并不是“輪子”的創(chuàng)造者,而是只要使用輪子即可。所以在本書中我們不去研究GIF分解合成算法的具體實現(xiàn)方式,而是將注意力聚焦在如何使用ImageIO框架實現(xiàn)需要的功能上。

2 GIF圖片分解代碼實現(xiàn)

  在正式分析代碼之前,先來看看整個工程的文件結(jié)構(gòu),如圖。 
 iOS的GIF動畫效果實現(xiàn)
  源文件使用的是plane.gif文件。ViewController.swift文件中的viewDidLoad()方法中包含了GIF圖片分解為單幀圖片并保存到本地的所有代碼。下面就結(jié)合“GIF分解為單幀圖片的過程”來實現(xiàn)這一功能。 
功能模塊一:讀取GIF文件并將之轉(zhuǎn)換為NSdata類型。

1   let gifPath:NSString = Bundle.main.path(forResource: "plane", ofType: "gif")! as NSString2   let gifData:Data = try! Data(contentsOf: URL(fileURLWithPath: gifPath as String))

  代碼第1行通過path方法獲取文件名為plane、文件格式為gif的文件地址。第2行獲取文件信息并加載到gifData(NSData類型)變量中。至此已經(jīng)完成整個處理流程的第一個環(huán)節(jié)。 
 iOS的GIF動畫效果實現(xiàn)
  功能模塊二:利用ImageIO框架,遍歷所有GIF子幀。需要注意的是使用ImageIO必須把讀取到的NSdata數(shù)據(jù)轉(zhuǎn)換為ImageIO可以處理的數(shù)據(jù)類型,這里使用CGImageSourceRef實現(xiàn)。其相應(yīng)功能模塊的處理流程如下所示。 
 iOS的GIF動畫效果實現(xiàn)

1   let gifDataSource:CGImageSource =
           CGImageSourceCreateWithData(gifData as CFData, nil)!2   let gifImageCount:Int = CGImageSourceGetCount(gifDataSource)3     for i in 0...gifImageCount-1{            let p_w_picpathref:CGImage? =CGImageSourceCreateImageAtIndex(gifDataSource, i, nil)            let p_w_picpath:UIImage = UIImage(cgImage: p_w_picpathref!,scale:UIScreen.main.scale,orientation:UIImageOrientation.up )
        }

  下面是GIF數(shù)據(jù)處理流程中ImageIO部分功能描述。代碼第1行實現(xiàn)將GIF原始數(shù)據(jù)類型NSdata轉(zhuǎn)換為ImageIO可以直接處理的數(shù)據(jù)類型CGImageSourceRef。第2行獲取當(dāng)前GIF圖片的分幀個數(shù)。我們知道GIF圖片都是由一幀幀圖片組成的,那么這一行就是為了獲取構(gòu)成GIF圖片的張數(shù)。第3行對CGImageSource數(shù)據(jù)按照圖片的序號進(jìn)行遍歷,將遍歷出的結(jié)果使用UIImage系統(tǒng)方法將之轉(zhuǎn)換為UIImage。 
  這里重點為大家介紹兩種方法。 
  CGImageSourceCreateImageAtIndex方法的作用是返回GIF中其中某一幀圖像的CGImage類型數(shù)據(jù)。該方法有三個參數(shù),參數(shù)1為GIF原始數(shù)據(jù),參數(shù)2 為GIF子幀中的序號(該序號從0開始),參數(shù)3為GIF數(shù)據(jù)提取的一些選擇參數(shù),因為這里不是很常用,所以設(shè)置為nil。

public func CGImageSourceCreateImageAtIndex(_ isrc: CGImageSource, _ index: Int, _ options: CFDictionary?) -> CGImage?

  以下為UIImage類的方法,這個方法用于實例化UIImage實例對象。該方法有三個參數(shù),參數(shù)1為需要構(gòu)建UIImage的內(nèi)容,注意這里的內(nèi)容是CGImage類型,參數(shù)2為手機(jī)物理像素與手機(jī)和手機(jī)顯示分辨率的換算系數(shù),參數(shù)3表明構(gòu)建的UIImage的圖像方向。通過這個方法就可以在某種手機(jī)分辨率下構(gòu)建指定方向的圖像,當(dāng)然圖像的類型是UIImage類型。

public init(CGImage cgImage: CGImage, scale: CGFloat, orientation: UIImageOrientation)

  通過上述兩步已經(jīng)獲取了UIImage,然而UIImage并不是通常我們看到的圖像格式,此圖像格式最大的特點是無法存儲為本地可以查看的圖片格式,因此如果需要將圖像保存在本地,就需要在這之前將已經(jīng)得到的UIImage數(shù)據(jù)類型轉(zhuǎn)換為PNG或者JPG類型的圖像數(shù)據(jù),然后才能把圖像存儲到本地。 
  下面是完整的GIF圖像分解保存代碼:

override func viewDidLoad() {1        super.viewDidLoad()2        let gifPath:NSString = Bundle.main.path(forResource:"plane", ofType: "gif")! as NSString3        let gifData:Data = try! Data(contentsOf:URL(fileURLWithPath: gifPath as String))4        let gifDataSource:CGImageSource =CGImageSourceCreateWithData(gifData as CFData, nil)!5        let gifImageCount:Int =CGImageSourceGetCount(gifDataSource)6        for i in 0...gifImageCount-1{7            let p_w_picpathref:CGImage? =CGImageSourceCreateImageAtIndex(gifDataSource, i, nil)8            let p_w_picpath:UIImage = UIImage(cgImage: p_w_picpathref!,scale:UIScreen.main.scale,orientation:UIImageOrientation.up )9            let p_w_picpathData:Data = UIImagePNGRepresentation(p_w_picpath)!10           var docs=NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)11           let documentsDirectory = docs[0] as String12           let p_w_picpathPath = documentsDirectory+"/\(i)"+".png"13           try? p_w_picpathData .write(to: URL(fileURLWithPath:p_w_picpathPath), options: [.atomic])14           print("\(p_w_picpathPath)")

        }
    }

  代碼第1行使用UIImagePNGRepresentation方法將UIImage數(shù)據(jù)類型存儲為PNG格式的data數(shù)據(jù)類型,第2行代碼和第3行代碼獲取應(yīng)用的Document目錄,第4行調(diào)用write方法將圖片寫入到本地文件中。如果大家想查看最終寫入的效果,可以在最后一行添加print信息,將文件寫入路徑打印出來,觀察圖像寫入是否成功。

3 GIF圖片分解最終實現(xiàn)效果

  通過上述代碼中的最后一行print(“(p_w_picpathPath)”)可以獲取圖片最終保存的路徑。進(jìn)入該路徑下可以看到下圖所示的圖片最終分解結(jié)果。 
 iOS的GIF動畫效果實現(xiàn)

  根據(jù)上下圖,在Mac系統(tǒng)下,利用系統(tǒng)圖片的查看工具來查看GIF圖片的分幀結(jié)果,對比圖中內(nèi)容,可以看出GIF圖片分解的結(jié)果是正確的。 
 iOS的GIF動畫效果實現(xiàn)

序列圖像合成GIF圖像

1 GIF圖片合成思路

  多幀圖像合成GIF的過程和GIF分解多幀圖像的過程互逆,GIF圖片分解過程倒過來推,就是GIF圖像合成的過程。這里將上面分解的67張序列單幀圖像作為需要處理的輸入源進(jìn)行講述。 
  從功能上來說,GIF圖片的合成分為以下三個主要部分。 
(1)加載待處理的67張原始數(shù)據(jù)源。 
(2)在Document目錄下構(gòu)建GIF文件。 
(3)設(shè)置GIF文件屬性,利用ImageIO編碼GIF文件。

2 GIF圖片合成代碼實現(xiàn)

  如下代碼是根據(jù)GIF構(gòu)建的三個主要步驟進(jìn)行編寫的。第一部分代碼的功能是將67張PNG圖片讀取到NSMutableArray數(shù)組中。代碼第1行初始化可變數(shù)組,第2行遍歷67張本地圖片,第3行按照圖片的命名規(guī)律,構(gòu)建67張圖片名稱,第4行加載本地圖片。最后一行將讀取的圖片依次加載到p_w_picpaths可變數(shù)組中。

        // Part1:讀取67張png圖片1        let p_w_picpaths:NSMutableArray = NSMutableArray()2        for i in 0...66{// 遍歷本地67張圖片3            let p_w_picpathPath = "\(i).png" // 構(gòu)建圖片名稱4            let p_w_picpath:UIImage = UIImage(named: p_w_picpathPath)!//5            p_w_picpaths.addObject(p_w_picpath)// 將圖片添加到數(shù)組中}

  代碼第二部分的功能是構(gòu)建在Document目錄下的GIF文件路徑。具體實現(xiàn)如下所示。

        // Part2:在Document目錄創(chuàng)建gif文件1      var docs=NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)2      let documentsDirectory = docs[0] as String3      let gifPath = documentsDirectory+"/plane.gif"4      print("\(gifPath)")5      let url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, gifPath as CFString!,CFURLPathStyle.cfurlposixPathStyle, false)6     let destion = CGImageDestinationCreateWithURL(url!, kUTTypeGIF, p_w_picpaths.count, nil)

  代碼1一行和第2行獲取Document路徑地址,第3行代碼通過字符串拼接時組成完整的Document路徑下plane.gif文件路徑。為了方便查看GIF文件所在路徑,第4行代碼將GIF文件路徑打印出來。第5行代碼將plane.gif文件路徑由string類型轉(zhuǎn)換為URL類型。最后一行代碼是ImageIO中構(gòu)建GIF圖片非常重要的方法,我們重點來分析該方法的作用和功能。

public func CGImageDestinationCreateWithURL(_ url: CFURL, _ type: CFString, _ count: Int, _ options: CFDictionary?) -> CGImageDestination?

CGImageDestinationCreateWithURL方法的作用是創(chuàng)建一個圖片的目標(biāo)對象,為了便于大家理解,這里把圖片目標(biāo)對象比喻為一個集合體。 
 iOS的GIF動畫效果實現(xiàn)
                      CGImageDestination結(jié)構(gòu) 
  集合體中描述了構(gòu)成當(dāng)前圖片目標(biāo)對象的一系列參數(shù),如圖片的URL地址、圖片類型、圖片幀數(shù)、配置參數(shù)等。本代碼中將plane.gif的本地文件路徑作為參數(shù)1傳遞給這個圖片目標(biāo)對象,參數(shù)2描述了圖片的類型為GIF圖片,參數(shù)3表明當(dāng)前GIF圖片構(gòu)成的幀數(shù),參數(shù)4暫時給它一個空值。 
  到目前為止,待處理圖片源已經(jīng)加載到代碼中,GIF圖片Destination也已經(jīng)完成構(gòu)建,下面就需要使用ImageIO框架把多幀PNG圖片編碼到GIF圖片中,其處理流程如下。 
 iOS的GIF動畫效果實現(xiàn)
  具體實現(xiàn)代碼如下:

 // Part3:設(shè)置gif圖片屬性,利用67張png圖片構(gòu)建gif1    let cgp_w_picpathPropertiesDic = [kCGImagePropertyGIFDelayTime as String:0.1]//設(shè)置每幀之間播放時間2    let cgp_w_picpathPropertiesDestDic =[kCGImagePropertyGIFDictionary as String:cgp_w_picpathPropertiesDic];3    for cgp_w_picpath in p_w_picpaths{4            CGImageDestinationAddImage(destion!, (cgp_w_picpath as AnyObject).cgImage!!,cgp_w_picpathPropertiesDestDic as CFDictionary?);}// 依次為gif圖像對象添加每一幀元素5    let gifPropertiesDic:NSMutableDictionary =NSMutableDictionary()6    gifPropertiesDic.setValue(kCGImagePropertyColorModelRGB,forKey: kCGImagePropertyColorModel as String)7    gifPropertiesDic.setValue(16, forKey:kCGImagePropertyDepth as String)// 設(shè)置圖像的顏色深度8    gifPropertiesDic.setValue(1, forKey:kCGImagePropertyGIFLoopCount as String)// 設(shè)置Gif執(zhí)行次數(shù)9    let gifDictionaryDestDic = [kCGImagePropertyGIFDictionary as String:gifPropertiesDic]10   CGImageDestinationSetProperties(destion!,gifDictionaryDestDic as CFDictionary?);//為gif圖像設(shè)置屬性11   CGImageDestinationFinalize(destion!);

  代碼第1行設(shè)置GIF圖片屬性,設(shè)置當(dāng)前GIF中每幀圖片展示時間間隔為0.1s。代碼第2行構(gòu)建一個GIF圖片屬性字典,字典使用GIF每幀之間的時間間隔初始化。代碼第4行使用遍歷的方法將已經(jīng)準(zhǔn)備好的圖片快速追加到GIF圖片的Destination中。代碼第5行初始化一個可變字典對象,該字典對象主要用于設(shè)置GIF圖片中每幀圖片屬性。第6行設(shè)置圖片彩色空間格式為RGB(Red Green Blue三基色)類型。第7行設(shè)置圖片顏色深度。一般來說黑白圖像也稱為二值圖像,顏色深度為1,表示2的一次方,即兩種顏色:黑和白?;叶葓D像一般顏色深度為8,表示2的8次方,共計256種顏色,即從黑色到白色的漸變過程有256種。對于彩×××片來說一般有16位深度和32位深度之說,這里設(shè)置為16位深度彩×××片。代碼第8行設(shè)置GIF圖片執(zhí)行的次數(shù),這里設(shè)置為執(zhí)行一次。代碼第9行和第10行負(fù)責(zé)將以上圖片設(shè)置的各種屬性添加到GIF的Destination目標(biāo)中。最后一行完成GIF的Destination目標(biāo)文件構(gòu)建。 
  可以打印出當(dāng)前GIF圖片的路徑,在該路徑下可以看到最終生成的GIF圖片。 
 iOS的GIF動畫效果實現(xiàn)

Gif圖像展示

  iOS原生并不支持直接顯示GIF圖片,由前面的分析可知,GIF圖片由一幀幀的單幀圖片構(gòu)成,所以只要實現(xiàn)GIF圖片的分解,接下來就是多組圖片顯示的問題了。為大家介紹另外一種圖片展現(xiàn)形式,即基于UIImageView展現(xiàn)GIF多幀圖片。 
經(jīng)過對GIF圖片展示思路的分析可以知道,在iOS下展現(xiàn)GIF分為兩步:第一步分解GIF圖片為單幀圖片,第二步在iOS下展現(xiàn)多幀圖片。UIImageView是一個用來展現(xiàn)圖片的UI組件,不過它還有一些動畫屬性可以用來進(jìn)行逐幀動畫展現(xiàn)。 
考慮到第一步GIF圖片已經(jīng)分解,所以這里把分解之后的67張圖片先加載進(jìn)來。 
 iOS的GIF動畫效果實現(xiàn)
  UIImageView多幀圖像展示具體實現(xiàn)代碼如下。

1       var p_w_picpaths:[UIImage] = []2        for i in 0...66{// 遍歷本地67張圖片3            let p_w_picpathPath = "\(i).png" // 構(gòu)建圖片名稱4            let p_w_picpath:UIImage = UIImage(named: p_w_picpathPath)!5            p_w_picpaths.append(p_w_picpath)// 將圖片添加到數(shù)組中
        }6        let p_w_picpathView = UIImageView()7        p_w_picpathView.frame = self.view.bounds8        p_w_picpathView.contentMode = UIViewContentMode.Center9        self.view.addSubview(p_w_picpathView)10       p_w_picpathView.animationImages = p_w_picpaths11       p_w_picpathView.animationDuration = 512       p_w_picpathView.animationRepeatCount = 113       p_w_picpathView.startAnimating()

  代碼第1行初始化一個子元素為UIImage類型的數(shù)組對象。第2行到第5行通過for循環(huán)將67張圖片依次加載到當(dāng)前數(shù)組中。第6行實例化一個UIImageView實例對象。第7行和第8行設(shè)置UIImageView實例對象的frame位置屬性以及圖片的拉伸方式,這里設(shè)置為居中顯示。第9行將UIImageView添加到self.view圖層上。第10行將初始化加載的67張圖片添加到UIImageView實例的animationImages上,相當(dāng)于設(shè)置UIImageView的內(nèi)容。第11行設(shè)置UIImageView圖片動畫播放周期。第12行設(shè)置動畫重復(fù)次數(shù)。最后一行啟動UIImageView多幀圖片展示動畫。 
 iOS的GIF動畫效果實現(xiàn)
  本文選自《iOS動畫——核心技術(shù)與案例實戰(zhàn)》,點此鏈接可在博文視點官網(wǎng)查看此書。

                 

 iOS的GIF動畫效果實現(xiàn)

  想及時獲得更多精彩文章,可在微信中搜索“博文視點”或者掃描下方二維碼并關(guān)注。
                    iOS的GIF動畫效果實現(xiàn)



向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI