一、GIF概念和特點(diǎn)
- 簡(jiǎn)介:
GIF是一種常用于動(dòng)畫效果的圖片格式肋乍。
GIF的原義是“圖像互換格式”鹅颊,是CompuServe公司在1987年開發(fā)的圖像文件格式,與常見的靜態(tài)圖像村塾格式JPG墓造、PNG類似堪伍,GIF是一種常用于動(dòng)態(tài)圖片的儲(chǔ)存的格式。 - 特點(diǎn):可以通過單幀圖像合成GIF或?qū)IF盡心單幀分解觅闽。
二帝雇、GIF的使用場(chǎng)景
- 1、GIF圖片分解為單幀圖片蛉拙;
- 2尸闸、一系列單幀圖片合成GIF圖片;
- 3孕锄、iOS系統(tǒng)上展示GIF動(dòng)畫效果吮廉。
三、GIF的使用
1畸肆、GIF分解單幀圖片
整個(gè)過程分為5個(gè)模塊宦芦、4個(gè)過程,分別如下轴脐。
- (1)本地讀取GIF圖片调卑,將其轉(zhuǎn)換為NSData數(shù)據(jù)類型抡砂;
- (2)將NSData作為ImageIO模塊的輸入;
- (3)獲取ImageIO的輸出數(shù)據(jù):UIImage;
- (4)將獲取到的UIImage數(shù)據(jù)存儲(chǔ)為JPG或PNG格式保存到本地恬涧。
在整個(gè)GIF圖片分解的過程中舀患,ImageIO是處理過程的核心部分。它負(fù)責(zé)對(duì)GIF文件格式進(jìn)行解析气破,并將解析之后的數(shù)據(jù)轉(zhuǎn)換為一幀幀圖片輸出聊浅。
代碼解析過程
- 讀取GIF文件并將之轉(zhuǎn)換為NSData類型:
/// 從本地讀取gif圖片 ==> 生成data數(shù)據(jù)
func loadGifInLocal(gifName: String?) -> Data? {
guard gifName != nil else {
return nil
}
let gifPath = Bundle.main.path(forResource: gifName, ofType: "gif")
guard gifPath != nil else {
print("文件名為" + gifName! + "的gif圖不存在!")
return nil
}
var gifData: Data = Data.init()
do {
gifData = try Data.init(contentsOf: URL.init(fileURLWithPath: gifPath!))
} catch {
print(error)
}
return gifData
}
- 利用ImageIO框架现使,遍歷所有GIF子幀低匙。需要注意的是使用ImageIO必須吧讀取到的NSData數(shù)據(jù)換成為ImageIO可以處理的數(shù)據(jù)類型,這里使用CGImageSourceRef實(shí)現(xiàn)碳锈。
/// 將data圖片數(shù)據(jù)轉(zhuǎn)換成image
func createImage(gifData: Data?) -> NSArray? {
guard gifData != nil else {
return nil
}
let gifDataSource: CGImageSource = CGImageSourceCreateWithData(gifData! as CFData, nil)!
let gifImageCount: NSInteger = CGImageSourceGetCount(gifDataSource)
let images: NSMutableArray = NSMutableArray.init()
for index in 0...gifImageCount-1 {
let imageref: CGImage? = CGImageSourceCreateImageAtIndex(gifDataSource, index, nil)
let image: UIImage = UIImage.init(cgImage: imageref!, scale: UIScreen.main.scale, orientation: UIImageOrientation.up)
images.add(image)
}
return images
}
其中CGImageSourceCreateImageAtIndex方法的作用是返回GIF中其中共某一幀圖像的CGImage類型數(shù)據(jù)顽冶。該方法有三個(gè)參數(shù),參數(shù)1位GIF原始數(shù)據(jù)售碳,參數(shù)2位GIF子幀中的序號(hào)(該序號(hào)從0開始)强重,參數(shù)3位GIF數(shù)據(jù)提取的一些選擇參數(shù),因?yàn)檫@里不是很常用贸人,所以設(shè)置為nil间景。
其中用UIImage類的實(shí)例化方法來實(shí)例化UIImage對(duì)象,該方法有三個(gè)參數(shù)艺智,參數(shù)1位需要構(gòu)建UIImage的內(nèi)容倘要,注意這里的內(nèi)容是CGImage類型,參數(shù)2為手機(jī)物理像素與手機(jī)和手機(jī)顯示分辨率的換算系數(shù)十拣,參數(shù)3標(biāo)明構(gòu)建的UIImage的圖像方向封拧。通過這個(gè)方法就可以在某種手機(jī)分辨率下構(gòu)建指定方向的圖像,當(dāng)然圖像的類型是UIIamge類型夭问。
- 保存UIImage圖像為png格式的圖像到沙盒中
// 將image圖片保存到本地沙盒
func saveImagesToLocal(imageArray images: Array<UIImage>?) -> () {
guard images?.count != 0 else {
return
}
for image in images! {
let index = images?.index(of: image)
let imageData: Data = UIImagePNGRepresentation(image)!
let docs = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory: String = docs[0] as String
let imagePath = documentsDirectory + "/shopping\(index ?? 0)" + ".png"
try? imageData.write(to: URL.init(fileURLWithPath: imagePath), options: [.atomic])
print(imagePath)
}
}
其中UIImagePNGRepresentation方法將UImage數(shù)據(jù)類型存儲(chǔ)為PNG格式的data數(shù)據(jù)類型泽西,并保存到沙盒中。
- GIF圖片分解最終實(shí)現(xiàn)效果
三缰趋、PNG圖片集合成GIF圖片
GIF圖片的合成三步驟:
- (1)加載待處理的原始數(shù)據(jù)源捧杉;
- (2)在Document目錄下構(gòu)建GIF文件;
- (3)設(shè)置GIF文件屬性埠胖,利用ImageIO編碼GIF文件糠溜。
代碼實(shí)現(xiàn):
// 1. 加載本地圖片
func loadImages() -> NSArray {
let imageArray: NSMutableArray = NSMutableArray()
for i in 1...25 {
let image = UIImage.init(named: "refresh_gif\(i).png")
if image != nil {
imageArray.add(image!)
}
}
return imageArray
}
// 2.創(chuàng)建Gif圖在Document中的保存路徑
func creatGifPath() -> String {
let docs = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let gifPath = docs[0] as String + "/refresh.gif"
print(gifPath)
return gifPath
}
// 3.設(shè)置GIF屬性,利用ImageIO編碼GIF文件
func saveGifToDocument(imageArray images: NSArray, _ gifPath: String) -> Bool {
guard images.count > 0 &&
gifPath.characters.count > 0 else {
return false
}
let url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, gifPath as CFString, .cfurlposixPathStyle, false)
let destion = CGImageDestinationCreateWithURL(url!, kUTTypeGIF, images.count, nil)
// 設(shè)置gif圖片屬性
// 設(shè)置每幀之間播放的時(shí)間0.1
let delayTime = [kCGImagePropertyGIFDelayTime as String:0.1]
let destDic = [kCGImagePropertyGIFDictionary as String:delayTime]
// 依次為gif圖像對(duì)象添加每一幀屬性
for image in images {
CGImageDestinationAddImage(destion!, (image as AnyObject).cgImage!!, destDic as CFDictionary?)
}
let propertiesDic: NSMutableDictionary = NSMutableDictionary()
propertiesDic.setValue(kCGImagePropertyColorModelRGB, forKey: kCGImagePropertyColorModel as String)
propertiesDic.setValue(16, forKey: kCGImagePropertyDepth as String) // 設(shè)置圖片的顏色深度
propertiesDic.setValue(1, forKey: kCGImagePropertyGIFLoopCount as String) // 設(shè)置Gif執(zhí)行次數(shù)
let gitDestDic = [kCGImagePropertyGIFDictionary as String:propertiesDic] // 為gif圖像設(shè)置屬性
CGImageDestinationSetProperties(destion!, gitDestDic as CFDictionary?)
CGImageDestinationFinalize(destion!)
return true
}
需要導(dǎo)入 import MobileCoreServices 框架直撤;
其中CGImageDestinationCreateWithURL方法的作用是創(chuàng)建一個(gè)圖片的目標(biāo)對(duì)象非竿,四個(gè)參數(shù)分別代表圖片的URL地址、圖片類型谋竖、圖片的幀數(shù)和配置信息等红柱。
iOS播放Gif的方式介紹
這里提供五種方式:
- 使用DispatchSource創(chuàng)建定時(shí)器播放gif圖(推薦)承匣;
- 使用UIImageView直接展示;
- 基于Timer定時(shí)器的逐幀動(dòng)畫效果锤悄;
- 基于CADisplaylink的逐幀動(dòng)畫效果韧骗;
- 使用WebView直接加載gif圖。
let kImagesCount: NSInteger = 24 // 圖片集個(gè)數(shù)
lazy var imageView: UIImageView = {
let imageView: UIImageView = UIImageView()
return imageView
}()
var timer: Timer?
var displayLink: CADisplayLink?
var index: NSInteger = 1
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(imageView)
imageView.center = self.view.center;
imageView.bounds.size = CGSize.init(width: self.view.bounds.size.width, height: 50)
// 方法一:使用UIimageView播放gif圖片
// self.showGifByUIImageView(images: self.loadImages())
// 方法二:使用定時(shí)器Timer播放gif圖片
// self.showGifByTimer()
// 方法三:使用CADisplayLink播放gif圖片
// self.showGifByCADisplayLink()
// 方式四:用WebView直接加載gif圖
// self.view.addSubview(self.webView)
// self.webView.frame = self.view.bounds
// self.showGifByWebView(gifName: "shopping")
// 方式五:用DispatchSource創(chuàng)建定時(shí)器零聚,播放Gif圖
imageView.frame = self.view.bounds
let url = Bundle.main.path(forResource: "shopping.gif", ofType: nil)
guard url != nil else {
return
}
self.showGifByDispatchSource(url: url!)
}
/// 釋放定時(shí)器
deinit {
timer?.invalidate()
timer = nil
}
// MARK: - 播放Gif圖
extension ViewController {
// 方法一:使用UIimageView播放gif圖片
func showGifByUIImageView(images: NSArray) -> () {
if images.count > 0 {
self.imageView.animationImages = images as? [UIImage]
self.imageView.animationDuration = 2.0
self.imageView.animationRepeatCount = 300
self.imageView.startAnimating()
}
}
// 方法二:使用定時(shí)器Timer播放gif圖片
func showGifByTimer() -> () {
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(refreshImageView), userInfo: nil, repeats: true)
RunLoop.current.add(timer!, forMode: RunLoopMode.commonModes)
}
// 方法三:使用CADisplayLink播放gif圖片
func showGifByCADisplayLink() -> () {
displayLink = CADisplayLink.init(target: self, selector: #selector(refreshImageView))
displayLink?.frameInterval = 6
displayLink?.add(to: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
}
func loadImages() -> NSArray {
let imageArray: NSMutableArray = NSMutableArray()
for i in 1...kImagesCount {
let image = UIImage.init(named: "refresh_gif\(i).png")
if image != nil {
imageArray.add(image!)
}
}
return imageArray
}
// 方式四:用WebView直接加載gif圖袍暴,進(jìn)行播放
func showGifByWebView(gifName: String) -> () {
guard gifName.characters.count > 0 else {
return
}
let path: String = Bundle.main.path(forResource: gifName, ofType: "gif") ?? ""
let gifData = NSData.dataWithContentsOfMappedFile(path)
if gifData != nil {
self.webView.load(gifData as! Data, mimeType: "image/gif", textEncodingName: "", baseURL: NSURL() as URL)
} else {
print("GIF圖名為:" + gifName + "的圖不存在!")
}
}
// 方式五:用DispatchSource創(chuàng)建定時(shí)器隶症,播放Gif圖
/// 根據(jù)DispatchSource創(chuàng)建定時(shí)器政模,播放Gif圖
///
/// - Parameter url: 資源路徑(網(wǎng)絡(luò)或本地)
private func showGifByDispatchSource(url: String) {
guard url.count > 0 else {
return
}
var data: Data?
if self.checkUrlIsNetWorkData(urlString: url) { // 網(wǎng)絡(luò)資源
data = try? Data.init(contentsOf: URL.init(string: url)!)
} else { // 本地資源
data = try? NSData.init(contentsOfFile: url) as Data
}
guard let imageData = data else { return }
let type = GifImageOperation.checkDataType(data: imageData)
if type == .gif {
let gifView = GifImageOperation.init(frame: imageView.bounds, gifData: imageData)
gifView.startAnimation()
imageView.addSubview(gifView)
} else {
imageView.image = UIImage.init(data: imageData)
}
}
/// 驗(yàn)證url資源是網(wǎng)絡(luò)還是本地資源
///
/// - Parameter urlString: 資源路徑
/// - Returns: 返回 true 表示為網(wǎng)絡(luò)資源,false 為本地資源
private func checkUrlIsNetWorkData(urlString: String?) -> Bool {
guard urlString != nil else { return false }
let regex = "http(s)?:\\/\\/([\\w-]+\\.)+[\\w-]+(\\/[\\w- .\\/?%&=]*)?"
let predicate = NSPredicate.init(format: "SELF MATCHES %@", regex)
return predicate.evaluate(with:urlString)
}
@objc func refreshImageView() -> () {
if index < 1 || index > kImagesCount {
index = 1
}
let image: UIImage = UIImage.init(named: "refresh_gif\(index).png")!
imageView.image = image
index += 1
}
}
- github地址: (https://github.com/SilongLi/GIFDemo)