Prechádzať zdrojové kódy

kf_indicator is now a protocol. Also added kf_indicatorType and two structs that conform to the Indicator protocol

João D. Moreira 9 rokov pred
rodič
commit
a19603394b

+ 8 - 0
Kingfisher.xcodeproj/project.pbxproj

@@ -19,6 +19,9 @@
 		4B98674F1CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; };
 		4B9867501CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; };
 		B43007AC86DBFFFD1AC6EDD1 /* libPods-KingfisherTests-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 798E024A9311DC80470CF240 /* libPods-KingfisherTests-tvOS.a */; };
+		CD2C73541D7F040B00A1D819 /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2C73531D7F040B00A1D819 /* Indicator.swift */; };
+		CD2C73551D7F040B00A1D819 /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2C73531D7F040B00A1D819 /* Indicator.swift */; };
+		CD2C73561D7F040B00A1D819 /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2C73531D7F040B00A1D819 /* Indicator.swift */; };
 		CD4593971D7EAB9900FD570E /* WrappedAssociatedObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4593961D7EAB9900FD570E /* WrappedAssociatedObjects.swift */; };
 		CD4593981D7EAB9900FD570E /* WrappedAssociatedObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4593961D7EAB9900FD570E /* WrappedAssociatedObjects.swift */; };
 		CD4593991D7EAB9900FD570E /* WrappedAssociatedObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4593961D7EAB9900FD570E /* WrappedAssociatedObjects.swift */; };
@@ -271,6 +274,7 @@
 		A8D69912DD16C2942EB1F40E /* Pods-KingfisherTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests/Pods-KingfisherTests.release.xcconfig"; sourceTree = "<group>"; };
 		A9E621E297FEFAD35D39C34E /* libPods-KingfisherTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KingfisherTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		B6B5C590A36C8E84C5B16C3E /* Pods-KingfisherTests-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests-tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests-tvOS/Pods-KingfisherTests-tvOS.debug.xcconfig"; sourceTree = "<group>"; };
+		CD2C73531D7F040B00A1D819 /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Indicator.swift; path = Sources/Indicator.swift; sourceTree = "<group>"; };
 		CD4593961D7EAB9900FD570E /* WrappedAssociatedObjects.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WrappedAssociatedObjects.swift; path = Sources/WrappedAssociatedObjects.swift; sourceTree = "<group>"; };
 		D10945EA1C526B6C001408EB /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Image.swift; sourceTree = "<group>"; };
 		D10945EB1C526B6C001408EB /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Sources/ImageCache.swift; sourceTree = "<group>"; };
@@ -465,6 +469,7 @@
 				D10945F61C526B6C001408EB /* UIButton+Kingfisher.swift */,
 				182FFF771CC9ACBA004B728D /* NSButton+Kingfisher.swift */,
 				CD4593961D7EAB9900FD570E /* WrappedAssociatedObjects.swift */,
+				CD2C73531D7F040B00A1D819 /* Indicator.swift */,
 			);
 			name = Sources;
 			sourceTree = "<group>";
@@ -1232,6 +1237,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				D109461A1C526C61001408EB /* Image.swift in Sources */,
+				CD2C73561D7F040B00A1D819 /* Indicator.swift in Sources */,
 				D109461B1C526C61001408EB /* ImageCache.swift in Sources */,
 				D109461C1C526C61001408EB /* ImageDownloader.swift in Sources */,
 				D109461D1C526C61001408EB /* ImageTransition.swift in Sources */,
@@ -1315,6 +1321,7 @@
 				D10946151C526C0D001408EB /* Resource.swift in Sources */,
 				D10946161C526C0D001408EB /* String+MD5.swift in Sources */,
 				D10946171C526C0D001408EB /* ThreadHelper.swift in Sources */,
+				CD2C73551D7F040B00A1D819 /* Indicator.swift in Sources */,
 				D10946181C526C0D001408EB /* UIButton+Kingfisher.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1373,6 +1380,7 @@
 				D10945FE1C526B86001408EB /* Resource.swift in Sources */,
 				D10945FF1C526B86001408EB /* String+MD5.swift in Sources */,
 				D10946001C526B86001408EB /* ThreadHelper.swift in Sources */,
+				CD2C73541D7F040B00A1D819 /* Indicator.swift in Sources */,
 				D10946011C526B86001408EB /* UIButton+Kingfisher.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 65 - 109
Sources/ImageView+Kingfisher.swift

@@ -28,11 +28,9 @@
 #if os(OSX)
 import AppKit
 typealias ImageView = NSImageView
-public typealias IndicatorView = NSProgressIndicator
 #else
 import UIKit
 typealias ImageView = UIImageView
-public typealias IndicatorView = UIActivityIndicatorView
 #endif
 
 // MARK: - Set Images
@@ -98,13 +96,8 @@ extension ImageView {
             return RetrieveImageTask.emptyTask
         }
         
-        let showIndicatorWhenLoading = kf_showIndicatorWhenLoading
-        var indicator: IndicatorView? = nil
-        if showIndicatorWhenLoading {
-            indicator = kf_indicator
-            indicator?.hidden = false
-            indicator?.kf_startAnimating()
-        }
+        let maybeIndicator = kf_indicator
+        maybeIndicator?.startAnimatingView()
         
         kf_setWebURL(resource.downloadURL)
         
@@ -129,7 +122,7 @@ extension ImageView {
                     sSelf.kf_setImageTask(nil)
                     
                     guard let image = image else {
-                        indicator?.kf_stopAnimating()
+                        maybeIndicator?.stopAnimatingView()
                         completionHandler?(image: nil, error: error, cacheType: cacheType, imageURL: imageURL)
                         return
                     }
@@ -139,7 +132,7 @@ extension ImageView {
                             #if !os(OSX)
                                 UIView.transitionWithView(sSelf, duration: 0.0, options: [],
                                     animations: {
-                                        indicator?.kf_stopAnimating()
+                                        maybeIndicator?.stopAnimatingView()
                                     },
                                     completion: { finished in
                                         UIView.transitionWithView(sSelf, duration: transition.duration,
@@ -155,7 +148,7 @@ extension ImageView {
                                 })
                             #endif
                     } else {
-                        indicator?.kf_stopAnimating()
+                        maybeIndicator?.stopAnimatingView()
                         sSelf.image = image
                         completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL)
                     }
@@ -184,10 +177,22 @@ extension ImageView {
     }
 }
 
+/**
+ Enum for the types of indicators that the user can choose from.
+ */
+extension ImageView {
+    public enum IndicatorType {
+        case none // no indicator
+        case custom(indicator: Indicator) // user custom indicator
+        case activity // activity indicator
+        case image(imageData: NSData) // supports gif
+    }
+}
+
 // MARK: - Associated Object
 private var lastURLKey: Void?
 private var indicatorKey: Void?
-private var showIndicatorWhenLoadingKey: Void?
+private var indicatorTypeKey: Void?
 private var imageTaskKey: Void?
 
 extension ImageView {
@@ -199,69 +204,62 @@ extension ImageView {
     private func kf_setWebURL(URL: NSURL) {
         setAssociatedObject(self, value: URL, associativeKey: &lastURLKey)
     }
-    
-    /// Whether show an animating indicator when the image view is loading an image or not.
-    /// Default is false.
-    public var kf_showIndicatorWhenLoading: Bool {
+
+
+    /// Holds which indicator type is going to be used.
+    /// Default is .none
+    public var kf_indicatorType: IndicatorType {
         get {
-            if let result:NSNumber = getAssociatedObject(self, associativeKey: &showIndicatorWhenLoadingKey) {
-                return result.boolValue
-            } else {
-                return false
-            }
+            let indicator: IndicatorType? = getAssociatedObject(self, associativeKey: &indicatorTypeKey)
+            return indicator ?? .none
         }
         
         set {
-            if kf_showIndicatorWhenLoading == newValue {
-                return
-            } else {
-                if newValue {
-                    
-#if os(OSX)
-                    let indicator = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
-    
-                    #if swift(>=2.3)
-                    indicator.controlSize = .Small
-                    #else
-                    indicator.controlSize = .SmallControlSize
-                    #endif
-                    indicator.style = .SpinningStyle
-#else
-    #if os(tvOS)
-                    let indicatorStyle = UIActivityIndicatorViewStyle.White
-    #else
-                    let indicatorStyle = UIActivityIndicatorViewStyle.Gray
-    #endif
-                    let indicator = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
-                    indicator.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleBottomMargin, .FlexibleTopMargin]
-#endif
+            switch newValue {
+            case .none:
+                kf_indicator = nil
+            case .activity:
+                kf_indicator = ActivityIndicator()
+            case .image(let data):
+                kf_indicator = ImageIndicator(imageData: data)
+            case .custom(let indicator):
+                kf_indicator = indicator
+            }
 
-                    indicator.kf_center = CGPoint(x: CGRectGetMidX(bounds), y: CGRectGetMidY(bounds))
-                    indicator.hidden = true
+            setAssociatedObject(self, value: newValue, associativeKey: &indicatorTypeKey)
+        }
+    }
 
-                    self.addSubview(indicator)
-                    
-                    kf_setIndicator(indicator)
-                } else {
-                    kf_indicator?.removeFromSuperview()
-                    kf_setIndicator(nil)
-                }
-                
-                setAssociatedObject(self, value:NSNumber(bool: newValue), associativeKey: &showIndicatorWhenLoadingKey)
+    /// `kf_indicator` holds any type that conforms to the protocol `Indicator`.
+    /// The protocol `Indicator` has a `view` property that will be shown when loading an image.
+    /// Everything will be `nil` if `kf_indicatorType` is .none.
+    public private(set) var kf_indicator: Indicator? {
+        get {
+            let indicator: (Indicator?)? = getAssociatedObject(self, associativeKey: &indicatorKey)
+            return indicator ?? nil
+        }
+
+        set {
+            // Remove previous
+            if let previousIndicator = kf_indicator {
+                previousIndicator.view.removeFromSuperview()
             }
+
+            // Add new
+            if var newIndicator = newValue {
+                newIndicator.view.frame = self.frame
+                newIndicator.viewCenter = CGPoint(x: bounds.midX, y: bounds.midY)
+                newIndicator.view.hidden = true
+                self.addSubview(newIndicator.view)
+            }
+
+            // Save in associated object
+            setAssociatedObject(self,
+                                value: newValue,
+                                associativeKey: &indicatorKey)
         }
     }
-    
-    /// The indicator view showing when loading. This will be `nil` if `kf_showIndicatorWhenLoading` is false.
-    /// You may want to use this to set the indicator style or color when you set `kf_showIndicatorWhenLoading` to true.
-    public var kf_indicator: IndicatorView? {
-        return getAssociatedObject(self, associativeKey: &indicatorKey)
-    }
-    
-    private func kf_setIndicator(indicator: IndicatorView?) {
-        setAssociatedObject(self, value: indicator, associativeKey: &indicatorKey)
-    }
-    
+
     private var kf_imageTask: RetrieveImageTask? {
         return getAssociatedObject(self, associativeKey: &imageTaskKey)
     }
@@ -270,45 +268,3 @@ extension ImageView {
         setAssociatedObject(self, value: task, associativeKey: &imageTaskKey)
     }
 }
-
-
-extension IndicatorView {
-    func kf_startAnimating() {
-        #if os(OSX)
-            startAnimation(nil)
-        #else
-            startAnimating()
-        #endif
-        hidden = false
-    }
-    
-    func kf_stopAnimating() {
-        #if os(OSX)
-            stopAnimation(nil)
-        #else
-            stopAnimating()
-        #endif
-        hidden = true
-    }
-    
-    #if os(OSX)
-    var kf_center: CGPoint {
-    get {
-    return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
-    }
-    set {
-    let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, y: newValue.y - frame.size.height / 2.0, width: frame.size.width, height: frame.size.height)
-    frame = newFrame
-    }
-    }
-    #else
-    var kf_center: CGPoint {
-        get {
-            return center
-        }
-        set {
-            center = newValue
-        }
-    }
-    #endif
-}

+ 176 - 0
Sources/Indicator.swift

@@ -0,0 +1,176 @@
+//
+//  Indicator.swift
+//  Kingfisher
+//
+//  Created by João D. Moreira on 30/08/16.
+//
+//  Copyright (c) 2016 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(OSX)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+#if os(OSX)
+    public typealias IndicatorView = NSView
+#else
+    public typealias IndicatorView = UIView
+#endif
+
+// MARK: - Indicator Protocol
+public protocol Indicator {
+    func startAnimatingView()
+    func stopAnimatingView()
+
+    var viewCenter: CGPoint { get set }
+    var view: IndicatorView { get }
+}
+
+extension Indicator {
+    #if os(OSX)
+    var viewCenter: CGPoint {
+        get {
+            let frame = view.frame
+            return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
+        }
+        set {
+            let frame = view.frame
+            let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0,
+                                  y: newValue.y - frame.size.height / 2.0,
+                                  width: frame.size.width,
+                                  height: frame.size.height)
+            view.frame = newFrame
+        }
+    }
+    #else
+    var viewCenter: CGPoint {
+        get {
+            return view.center
+        }
+        set {
+            view.center = newValue
+        }
+    }
+    #endif
+}
+
+// MARK: - ActivityIndicator
+// Displays a NSProgressIndicator / UIActivityIndicatorView
+struct ActivityIndicator: Indicator {
+
+    #if os(OSX)
+    private let activityIndicatorView: NSProgressIndicator
+    #else
+    private let activityIndicatorView: UIActivityIndicatorView
+    #endif
+
+    var view: IndicatorView {
+        return activityIndicatorView
+    }
+
+    func startAnimatingView() {
+        #if os(OSX)
+            activityIndicatorView.startAnimation(nil)
+        #else
+            activityIndicatorView.startAnimating()
+        #endif
+        activityIndicatorView.hidden = false
+    }
+
+    func stopAnimatingView() {
+        #if os(OSX)
+            activityIndicatorView.stopAnimation(nil)
+        #else
+            activityIndicatorView.stopAnimating()
+        #endif
+        activityIndicatorView.hidden = true
+    }
+
+    init() {
+        #if os(OSX)
+            activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
+
+            #if swift(>=2.3)
+                activityIndicatorView.controlSize = .Small
+            #else
+                activityIndicatorView.controlSize = .SmallControlSize
+            #endif
+            activityIndicatorView.style = .SpinningStyle
+        #else
+            #if os(tvOS)
+                let indicatorStyle = UIActivityIndicatorViewStyle.White
+            #else
+                let indicatorStyle = UIActivityIndicatorViewStyle.Gray
+            #endif
+            activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
+            activityIndicatorView.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleBottomMargin, .FlexibleTopMargin]
+        #endif
+    }
+}
+
+// MARK: - ImageIndicator
+// Displays an ImageView. Supports gif
+struct ImageIndicator: Indicator {
+    private let animatedImageIndicatorView: ImageView
+
+    var view: IndicatorView {
+        return animatedImageIndicatorView
+    }
+
+    init(imageData data: NSData) {
+
+        let image = Image.kf_imageWithData(data, scale: 1.0, preloadAllGIFData: true)
+        animatedImageIndicatorView = ImageView()
+        animatedImageIndicatorView.image = image
+        
+        #if os(OSX)
+            // Need for gif to animate on OSX
+            self.animatedImageIndicatorView.imageScaling = .ScaleNone
+            self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
+        #else
+            animatedImageIndicatorView.contentMode = .Center
+            
+            animatedImageIndicatorView.autoresizingMask = [.FlexibleLeftMargin,
+                                                           .FlexibleRightMargin,
+                                                           .FlexibleBottomMargin,
+                                                           .FlexibleTopMargin]
+        #endif
+    }
+
+    func startAnimatingView() {
+        #if os(OSX)
+            animatedImageIndicatorView.animates = true
+        #else
+            animatedImageIndicatorView.startAnimating()
+        #endif
+        animatedImageIndicatorView.hidden = false
+    }
+
+    func stopAnimatingView() {
+        #if os(OSX)
+            animatedImageIndicatorView.animates = false
+        #else
+            animatedImageIndicatorView.stopAnimating()
+        #endif
+        animatedImageIndicatorView.hidden = true
+    }
+}