added iOS source code
[wl-app.git] / iOS / Pods / Kingfisher / Sources / ImageView+Kingfisher.swift
diff --git a/iOS/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift b/iOS/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift
new file mode 100755 (executable)
index 0000000..cb230fb
--- /dev/null
@@ -0,0 +1,263 @@
+//
+//  ImageView+Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 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(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+// MARK: - Extension methods.
+/**
+ *    Set image to use from web.
+ */
+extension Kingfisher where Base: ImageView {
+    /**
+     Set an image with a resource, a placeholder image, options, progress handler and completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setImage(with resource: Resource?,
+                         placeholder: Placeholder? = nil,
+                         options: KingfisherOptionsInfo? = nil,
+                         progressBlock: DownloadProgressBlock? = nil,
+                         completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            self.placeholder = placeholder
+            setWebURL(nil)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil
+        
+        if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet.
+            self.placeholder = placeholder
+        }
+
+        let maybeIndicator = indicator
+        maybeIndicator?.startAnimatingView()
+        
+        setWebURL(resource.downloadURL)
+
+        if base.shouldPreloadAllAnimation() {
+            options.append(.preloadAllAnimationData)
+        }
+        
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.webURL else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: {[weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    maybeIndicator?.stopAnimatingView()
+                    guard let strongBase = base, imageURL == self.webURL else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    self.setImageTask(nil)
+                    guard let image = image else {
+                        completionHandler?(nil, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)),
+                        case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
+                    {
+                        self.placeholder = nil
+                        strongBase.image = image
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    #if !os(macOS)
+                        UIView.transition(with: strongBase, duration: 0.0, options: [],
+                                          animations: { maybeIndicator?.stopAnimatingView() },
+                                          completion: { _ in
+
+                                            self.placeholder = nil
+                                            UIView.transition(with: strongBase, duration: transition.duration,
+                                                              options: [transition.animationOptions, .allowUserInteraction],
+                                                              animations: {
+                                                                // Set image property in the animation.
+                                                                transition.animations?(strongBase, image)
+                                                              },
+                                                              completion: { finished in
+                                                                transition.completion?(finished)
+                                                                completionHandler?(image, error, cacheType, imageURL)
+                                                              })
+                                          })
+                    #endif
+                }
+            })
+        
+        setImageTask(task)
+        
+        return task
+    }
+    
+    /**
+     Cancel the image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelDownloadTask() {
+        imageTask?.cancel()
+    }
+}
+
+// MARK: - Associated Object
+private var lastURLKey: Void?
+private var indicatorKey: Void?
+private var indicatorTypeKey: Void?
+private var placeholderKey: Void?
+private var imageTaskKey: Void?
+
+extension Kingfisher where Base: ImageView {
+    /// Get the image URL binded to this image view.
+    public var webURL: URL? {
+        return objc_getAssociatedObject(base, &lastURLKey) as? URL
+    }
+    
+    fileprivate func setWebURL(_ url: URL?) {
+        objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    /// Holds which indicator type is going to be used.
+    /// Default is .none, means no indicator will be shown.
+    public var indicatorType: IndicatorType {
+        get {
+            let indicator = objc_getAssociatedObject(base, &indicatorTypeKey) as? IndicatorType
+            return indicator ?? .none
+        }
+        
+        set {
+            switch newValue {
+            case .none:
+                indicator = nil
+            case .activity:
+                indicator = ActivityIndicator()
+            case .image(let data):
+                indicator = ImageIndicator(imageData: data)
+            case .custom(let anIndicator):
+                indicator = anIndicator
+            }
+            
+            objc_setAssociatedObject(base, &indicatorTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    /// Holds any type that conforms to the protocol `Indicator`.
+    /// The protocol `Indicator` has a `view` property that will be shown when loading an image.
+    /// It will be `nil` if `indicatorType` is `.none`.
+    public fileprivate(set) var indicator: Indicator? {
+        get {
+            guard let box = objc_getAssociatedObject(base, &indicatorKey) as? Box<Indicator> else {
+                return nil
+            }
+            return box.value
+        }
+        
+        set {
+            // Remove previous
+            if let previousIndicator = indicator {
+                previousIndicator.view.removeFromSuperview()
+            }
+            
+            // Add new
+            if var newIndicator = newValue {
+                // Set default indicator frame if the view's frame not set.
+                if newIndicator.view.frame == .zero {
+                    newIndicator.view.frame = base.frame
+                }
+                newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY)
+                newIndicator.view.isHidden = true
+                base.addSubview(newIndicator.view)
+            }
+            
+            // Save in associated object
+            // Wrap newValue with Box to workaround an issue that Swift does not recognize
+            // and casting protocol for associate object correctly. https://github.com/onevcat/Kingfisher/issues/872
+            objc_setAssociatedObject(base, &indicatorKey, newValue.map(Box.init), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    fileprivate var imageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    public fileprivate(set) var placeholder: Placeholder? {
+        get {
+            return objc_getAssociatedObject(base, &placeholderKey) as? Placeholder
+        }
+        
+        set {
+            if let previousPlaceholder = placeholder {
+                previousPlaceholder.remove(from: base)
+            }
+            
+            if let newPlaceholder = newValue {
+                newPlaceholder.add(to: base)
+            } else {
+                base.image = nil
+            }
+            
+            objc_setAssociatedObject(base, &placeholderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+}
+
+
+@objc extension ImageView {
+    func shouldPreloadAllAnimation() -> Bool { return true }
+}