Quellcode durchsuchen

Compatible with earlier version

This commit allows the traditional way to play GIF with regular UIImageView. This could keep back compatibility for the framework so we could avoid releasing a new major version.
onevcat vor 9 Jahren
Ursprung
Commit
767f430a3b

+ 5 - 0
Sources/AnimatedImageView.swift

@@ -144,6 +144,11 @@ public class AnimatedImageView: UIImageView {
         didMove()
         didMove()
     }
     }
     
     
+    // This is for back compatibility that using regular UIImageView to show GIF.
+    override func shouldPreloadAllGIF() -> Bool {
+        return false
+    }
+    
     // MARK: - Private method
     // MARK: - Private method
     /// Reset the animator.
     /// Reset the animator.
     private func reset() {
     private func reset() {

+ 53 - 36
Sources/Image.swift

@@ -225,62 +225,79 @@ func ImagesCountWithImageSource(ref: CGImageSourceRef) -> Int {
 }
 }
 
 
 extension Image {
 extension Image {
-    static func kf_animatedImageWithGIFData(gifData data: NSData) -> Image? {
-        return kf_animatedImageWithGIFData(gifData: data, scale: 1.0, duration: 0.0)
+    static func kf_animatedImageWithGIFData(gifData data: NSData, preloadAll: Bool) -> Image? {
+        return kf_animatedImageWithGIFData(gifData: data, scale: 1.0, duration: 0.0, preloadAll: preloadAll)
     }
     }
     
     
-    static func kf_animatedImageWithGIFData(gifData data: NSData, scale: CGFloat, duration: NSTimeInterval) -> Image? {
+    static func kf_animatedImageWithGIFData(gifData data: NSData, scale: CGFloat, duration: NSTimeInterval, preloadAll: Bool) -> Image? {
         
         
+        func decodeFromSource(imageSource: CGImageSource, options: NSDictionary) -> ([Image], NSTimeInterval)? {
+
+            let frameCount = CGImageSourceGetCount(imageSource)
+            var images = [Image]()
+            var gifDuration = 0.0
+            for i in 0 ..< frameCount {
+                
+                guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else {
+                    return nil
+                }
+                
+                if frameCount == 1 {
+                    // Single frame
+                    gifDuration = Double.infinity
+                } else {
+                    // Animated GIF
+                    guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil),
+                        gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
+                        frameDuration = (gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber) else
+                    {
+                        return nil
+                    }
+                    gifDuration += frameDuration.doubleValue
+                }
+                
+                images.append(Image.kf_imageWithCGImage(imageRef, scale: scale, refImage: nil))
+            }
+            
+            return (images, gifDuration)
+        }
+        
+        // Start of kf_animatedImageWithGIFData
         let options: NSDictionary = [kCGImageSourceShouldCache as String: NSNumber(bool: true), kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF]
         let options: NSDictionary = [kCGImageSourceShouldCache as String: NSNumber(bool: true), kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF]
         guard let imageSource = CGImageSourceCreateWithData(data, options) else {
         guard let imageSource = CGImageSourceCreateWithData(data, options) else {
             return nil
             return nil
         }
         }
         
         
 #if os(OSX)
 #if os(OSX)
-        let frameCount = CGImageSourceGetCount(imageSource)
-        var images = [Image]()
-        
-        var gifDuration = 0.0
-        
-        for i in 0 ..< frameCount {
-            
-            guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else {
-                return nil
-            }
-            
-            if frameCount == 1 {
-                // Single frame
-                gifDuration = Double.infinity
-            } else {
-                // Animated GIF
-                guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil),
-                    gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
-                    frameDuration = (gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber) else
-                {
-                    return nil
-                }
-                gifDuration += frameDuration.doubleValue
-            }
-            
-            images.append(Image.kf_imageWithCGImage(imageRef, scale: scale, refImage: nil))
+        guard let (images, gifDuration) = decodeFromSource(imageSource, options: options) else {
+            return nil
         }
         }
-        
         let image = Image(data: data)
         let image = Image(data: data)
         image?.kf_images = images
         image?.kf_images = images
         image?.kf_duration = gifDuration
         image?.kf_duration = gifDuration
+    
         return image
         return image
 #else
 #else
-        let image = Image(data: data)
-        image?.kf_imageSource = ImageSource(ref: imageSource)
-        image?.kf_animatedImageData = data
-        return image
+    
+        if preloadAll {
+            guard let (images, gifDuration) = decodeFromSource(imageSource, options: options) else {
+                return nil
+            }
+            return Image.kf_animatedImageWithImages(images, duration: duration <= 0.0 ? gifDuration : duration)
+        } else {
+            let image = Image(data: data)
+            image?.kf_imageSource = ImageSource(ref: imageSource)
+            image?.kf_animatedImageData = data
+            return image
+        }
 #endif
 #endif
+        
     }
     }
 }
 }
 
 
 // MARK: - Create images from data
 // MARK: - Create images from data
 extension Image {
 extension Image {
-    static func kf_imageWithData(data: NSData, scale: CGFloat) -> Image? {
+    static func kf_imageWithData(data: NSData, scale: CGFloat, preloadAllGIFData: Bool) -> Image? {
         var image: Image?
         var image: Image?
         #if os(OSX)
         #if os(OSX)
             switch data.kf_imageFormat {
             switch data.kf_imageFormat {
@@ -293,7 +310,7 @@ extension Image {
             switch data.kf_imageFormat {
             switch data.kf_imageFormat {
             case .JPEG: image = Image(data: data, scale: scale)
             case .JPEG: image = Image(data: data, scale: scale)
             case .PNG: image = Image(data: data, scale: scale)
             case .PNG: image = Image(data: data, scale: scale)
-            case .GIF: image = Image.kf_animatedImageWithGIFData(gifData: data, scale: scale, duration: 0.0)
+            case .GIF: image = Image.kf_animatedImageWithGIFData(gifData: data, scale: scale, duration: 0.0, preloadAll: preloadAllGIFData)
             case .Unknown: image = Image(data: data, scale: scale)
             case .Unknown: image = Image(data: data, scale: scale)
             }
             }
         #endif
         #endif

+ 8 - 6
Sources/ImageCache.swift

@@ -286,7 +286,7 @@ extension ImageCache {
             var sSelf: ImageCache! = self
             var sSelf: ImageCache! = self
             block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) {
             block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) {
                 // Begin to load image from disk
                 // Begin to load image from disk
-                if let image = sSelf.retrieveImageInDiskCacheForKey(key, scale: options.scaleFactor) {
+                if let image = sSelf.retrieveImageInDiskCacheForKey(key, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) {
                     if options.backgroundDecode {
                     if options.backgroundDecode {
                         dispatch_async(sSelf.processQueue, { () -> Void in
                         dispatch_async(sSelf.processQueue, { () -> Void in
                             let result = image.kf_decodedImage(scale: options.scaleFactor)
                             let result = image.kf_decodedImage(scale: options.scaleFactor)
@@ -334,12 +334,14 @@ extension ImageCache {
     Get an image for a key from disk.
     Get an image for a key from disk.
     
     
     - parameter key: Key for the image.
     - parameter key: Key for the image.
-    - param scale: The scale factor to assume when interpreting the image data.
+    - parameter scale: The scale factor to assume when interpreting the image data.
+    - parameter preloadAllGIFData: Whether all GIF data should be loaded. If true, you can set the loaded image to a regular UIImageView to play 
+      the GIF animation. Otherwise, you should use `AnimatedImageView` to play it. Default is `false`
 
 
     - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
     - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
     */
     */
-    public func retrieveImageInDiskCacheForKey(key: String, scale: CGFloat = 1.0) -> Image? {
-        return diskImageForKey(key, scale: scale)
+    public func retrieveImageInDiskCacheForKey(key: String, scale: CGFloat = 1.0, preloadAllGIFData: Bool = false) -> Image? {
+        return diskImageForKey(key, scale: scale, preloadAllGIFData: preloadAllGIFData)
     }
     }
 }
 }
 
 
@@ -631,9 +633,9 @@ extension ImageCache {
 // MARK: - Internal Helper
 // MARK: - Internal Helper
 extension ImageCache {
 extension ImageCache {
     
     
-    func diskImageForKey(key: String, scale: CGFloat) -> Image? {
+    func diskImageForKey(key: String, scale: CGFloat, preloadAllGIFData: Bool) -> Image? {
         if let data = diskImageDataForKey(key) {
         if let data = diskImageDataForKey(key) {
-            return Image.kf_imageWithData(data, scale: scale)
+            return Image.kf_imageWithData(data, scale: scale, preloadAllGIFData: preloadAllGIFData)
         } else {
         } else {
             return nil
             return nil
         }
         }

+ 1 - 1
Sources/ImageDownloader.swift

@@ -449,7 +449,7 @@ class ImageDownloaderSessionHandler: NSObject, NSURLSessionDataDelegate, Authent
             if let fetchLoad = downloader.fetchLoadForKey(URL) {
             if let fetchLoad = downloader.fetchLoadForKey(URL) {
                 
                 
                 let options = fetchLoad.options ?? KingfisherEmptyOptionsInfo
                 let options = fetchLoad.options ?? KingfisherEmptyOptionsInfo
-                if let image = Image.kf_imageWithData(fetchLoad.responseData, scale: options.scaleFactor) {
+                if let image = Image.kf_imageWithData(fetchLoad.responseData, scale: options.scaleFactor, preloadAllGIFData: options.preloadAllGIFData) {
                     
                     
                     downloader.delegate?.imageDownloader?(downloader, didDownloadImage: image, forURL: URL, withResponse: task.response!)
                     downloader.delegate?.imageDownloader?(downloader, didDownloadImage: image, forURL: URL, withResponse: task.response!)
                     
                     

+ 12 - 1
Sources/ImageView+Kingfisher.swift

@@ -102,7 +102,12 @@ extension ImageView {
         
         
         kf_setWebURL(resource.downloadURL)
         kf_setWebURL(resource.downloadURL)
         
         
-        let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo,
+        var options = optionsInfo ?? []
+        if shouldPreloadAllGIF() {
+            options.append(.PreloadAllGIFData)
+        }
+
+        let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: options,
             progressBlock: { receivedSize, totalSize in
             progressBlock: { receivedSize, totalSize in
                 if let progressBlock = progressBlock {
                 if let progressBlock = progressBlock {
                     progressBlock(receivedSize: receivedSize, totalSize: totalSize)
                     progressBlock(receivedSize: receivedSize, totalSize: totalSize)
@@ -158,6 +163,12 @@ extension ImageView {
     }
     }
 }
 }
 
 
+extension ImageView {
+    func shouldPreloadAllGIF() -> Bool {
+        return true
+    }
+}
+
 extension ImageView {
 extension ImageView {
     /**
     /**
      Cancel the image download task bounded to the image view if it is running.
      Cancel the image download task bounded to the image view if it is running.

+ 17 - 9
Sources/KingfisherOptionsInfo.swift

@@ -49,6 +49,7 @@ Items could be added into KingfisherOptionsInfo.
 - BackgroundDecode: Decode the image in background thread before using.
 - BackgroundDecode: Decode the image in background thread before using.
 - CallbackDispatchQueue: The associated value of this member will be used as the target queue of dispatch callbacks when retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
 - CallbackDispatchQueue: The associated value of this member will be used as the target queue of dispatch callbacks when retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
 - ScaleFactor: The associated value of this member will be used as the scale factor when converting retrieved data to an image.
 - ScaleFactor: The associated value of this member will be used as the scale factor when converting retrieved data to an image.
+- PreloadAllGIFData: Whether all the GIF data should be preloaded. Default it false, which means following frames will be loaded on need. If true, all the GIF data will be loaded and decoded into memory. This option is mainly used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use corresponding image view type instead of setting this option.
 */
 */
 public enum KingfisherOptionsInfoItem {
 public enum KingfisherOptionsInfoItem {
     case TargetCache(ImageCache?)
     case TargetCache(ImageCache?)
@@ -60,6 +61,7 @@ public enum KingfisherOptionsInfoItem {
     case BackgroundDecode
     case BackgroundDecode
     case CallbackDispatchQueue(dispatch_queue_t?)
     case CallbackDispatchQueue(dispatch_queue_t?)
     case ScaleFactor(CGFloat)
     case ScaleFactor(CGFloat)
+    case PreloadAllGIFData
 }
 }
 
 
 infix operator <== {
 infix operator <== {
@@ -70,15 +72,17 @@ infix operator <== {
 // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
 // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
 func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
 func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
     switch (lhs, rhs) {
     switch (lhs, rhs) {
-    case (.TargetCache(_), .TargetCache(_)): return true
-    case (.Downloader(_), .Downloader(_)): return true
-    case (.Transition(_), .Transition(_)): return true
-    case (.DownloadPriority(_), .DownloadPriority(_)): return true
-    case (.ForceRefresh, .ForceRefresh): return true
-    case (.CacheMemoryOnly, .CacheMemoryOnly): return true
-    case (.BackgroundDecode, .BackgroundDecode): return true
-    case (.CallbackDispatchQueue(_), .CallbackDispatchQueue(_)): return true
-    case (.ScaleFactor(_), .ScaleFactor(_)): return true
+    case (.TargetCache(_), .TargetCache(_)): fallthrough
+    case (.Downloader(_), .Downloader(_)): fallthrough
+    case (.Transition(_), .Transition(_)): fallthrough
+    case (.DownloadPriority(_), .DownloadPriority(_)): fallthrough
+    case (.ForceRefresh, .ForceRefresh): fallthrough
+    case (.CacheMemoryOnly, .CacheMemoryOnly): fallthrough
+    case (.BackgroundDecode, .BackgroundDecode): fallthrough
+    case (.CallbackDispatchQueue(_), .CallbackDispatchQueue(_)): fallthrough
+    case (.ScaleFactor(_), .ScaleFactor(_)): fallthrough
+    case (.PreloadAllGIFData, .PreloadAllGIFData): return true
+        
     default: return false
     default: return false
     }
     }
 }
 }
@@ -142,6 +146,10 @@ extension CollectionType where Generator.Element == KingfisherOptionsInfoItem {
         return contains{ $0 <== .BackgroundDecode }
         return contains{ $0 <== .BackgroundDecode }
     }
     }
     
     
+    var preloadAllGIFData: Bool {
+        return contains { $0 <== .PreloadAllGIFData }
+    }
+    
     var callbackDispatchQueue: dispatch_queue_t {
     var callbackDispatchQueue: dispatch_queue_t {
         if let item = kf_firstMatchIgnoringAssociatedValue(.CallbackDispatchQueue(nil)),
         if let item = kf_firstMatchIgnoringAssociatedValue(.CallbackDispatchQueue(nil)),
             case .CallbackDispatchQueue(let queue) = item
             case .CallbackDispatchQueue(let queue) = item