Przeglądaj źródła

Use Source for NSButton

onevcat 7 lat temu
rodzic
commit
55667c629d

+ 36 - 43
Sources/Extensions/ImageView+Kingfisher.swift

@@ -44,7 +44,7 @@ extension KingfisherClass where Base: ImageView {
         guard let source = source else {
             self.placeholder = placeholder
             taskIdentifier = nil
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
 
@@ -72,7 +72,7 @@ extension KingfisherClass where Base: ImageView {
                 if let progressBlock = progressBlock {
                     progressBlock(receivedSize, totalSize)
                 }
-        },
+            },
             completionHandler: { result in
                 CallbackQueue.mainCurrentOrAsync.execute {
                     maybeIndicator?.stopAnimatingView()
@@ -87,8 +87,6 @@ extension KingfisherClass where Base: ImageView {
 
                     switch result {
                     case .success(let value):
-
-                        #if !os(macOS)
                         guard self.needsTransition(options: options, cacheType: value.cacheType) else {
                             self.placeholder = nil
                             self.base.image = value.image
@@ -96,33 +94,9 @@ extension KingfisherClass where Base: ImageView {
                             return
                         }
 
-                        let transition = options.transition
-
-                        // Force hiding the indicator without transition first.
-                        UIView.transition(
-                            with: self.base,
-                            duration: 0.0,
-                            options: [],
-                            animations: { maybeIndicator?.stopAnimatingView() },
-                            completion: { _ in
-                                self.placeholder = nil
-                                UIView.transition(
-                                    with: self.base,
-                                    duration: transition.duration,
-                                    options: [transition.animationOptions, .allowUserInteraction],
-                                    animations: { transition.animations?(self.base, value.image) },
-                                    completion: { finished in
-                                        transition.completion?(finished)
-                                        completionHandler?(result)
-                                }
-                                )
+                        self.makeTransition(image: value.image, transition: options.transition) {
+                            completionHandler?(result)
                         }
-                        )
-                        #else
-                        self.placeholder = nil
-                        self.base.image = value.image
-                        completionHandler?(result)
-                        #endif
                     case .failure:
                         if let image = options.onFailureImage {
                             self.base.image = image
@@ -136,7 +110,6 @@ extension KingfisherClass where Base: ImageView {
         return task
     }
 
-
     /// Sets an image to the image view with a requested resource.
     ///
     /// - Parameters:
@@ -183,20 +156,40 @@ extension KingfisherClass where Base: ImageView {
         imageTask?.cancel()
     }
 
-    #if os(iOS) || os(tvOS)
     private func needsTransition(options: KingfisherOptionsInfo, cacheType: CacheType) -> Bool {
-        guard let _ = options.lastMatchIgnoringAssociatedValue(.transition(.none)) else {
-            return false
-        }
-        if options.forceTransition {
-            return true
-        }
-        if cacheType == .none {
-            return true
-        }
+        guard let _ = options.lastMatchIgnoringAssociatedValue(.transition(.none)) else { return false }
+        if options.forceTransition { return true }
+        if cacheType == .none { return true }
         return false
     }
-    #endif
+
+
+    private func makeTransition(image: Image, transition: ImageTransition, done: @escaping () -> Void) {
+        #if !os(macOS)
+        // Force hiding the indicator without transition first.
+        UIView.transition(
+            with: self.base,
+            duration: 0.0,
+            options: [],
+            animations: { self.indicator?.stopAnimatingView() },
+            completion: { _ in
+                self.placeholder = nil
+                UIView.transition(
+                    with: self.base,
+                    duration: transition.duration,
+                    options: [transition.animationOptions, .allowUserInteraction],
+                    animations: { transition.animations?(self.base, image) },
+                    completion: { finished in
+                        transition.completion?(finished)
+                        done()
+                    }
+                )
+            }
+        )
+        #else
+        done()
+        #endif
+    }
 }
 
 // MARK: - Associated Object
@@ -208,7 +201,7 @@ private var imageTaskKey: Void?
 
 extension KingfisherClass where Base: ImageView {
 
-    public internal(set) var taskIdentifier: String? {
+    public private(set) var taskIdentifier: String? {
         get { return getAssociatedObject(base, &taskIdentifierKey) }
         set { setRetainedAssociatedObject(base, &taskIdentifierKey, newValue) }
     }

+ 129 - 82
Sources/Extensions/NSButton+Kingfisher.swift

@@ -28,62 +28,47 @@
 import AppKit
 
 extension KingfisherClass where Base: NSButton {
-    
-    /// Sets an image to the button with a requested resource.
-    ///
-    /// - Parameters:
-    ///   - resource: The `Resource` object contains information about the resource.
-    ///   - placeholder: A placeholder to show while retrieving the image from the given `resource`.
-    ///   - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more.
-    ///   - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an
-    ///                    `expectedContentLength`, this block will not be called.
-    ///   - completionHandler: Called when the image retrieved and set finished.
-    /// - Returns: A task represents the image downloading.
-    ///
-    /// - Note:
-    /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache
-    /// or network. Since this method will perform UI changes, you must call it from the main thread.
-    /// Both `progressBlock` and `completionHandler` will be also executed in the main thread.
-    ///
+
     @discardableResult
-    public func setImage(with resource: Resource?,
-                         placeholder: Image? = nil,
-                         options: KingfisherOptionsInfo? = nil,
-                         progressBlock: DownloadProgressBlock? = nil,
-                         completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil)
-        -> DownloadTask?
+    public func setImage(
+        with source: Source?,
+        placeholder: Image? = nil,
+        options: KingfisherOptionsInfo? = nil,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
     {
-        guard let resource = resource else {
+        guard let source = source else {
             base.image = placeholder
-            webURL = nil
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            taskIdentifier = nil
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
-        
+
         let options = KingfisherManager.shared.defaultOptions + (options ?? .empty)
         if !options.keepCurrentImageWhileLoading {
             base.image = placeholder
         }
-        
-        webURL = resource.downloadURL
+
+        taskIdentifier = source.identifier
+
         let task = KingfisherManager.shared.retrieveImage(
-            with: resource,
+            with: source,
             options: options,
             progressBlock: { receivedSize, totalSize in
-                guard resource.downloadURL == self.webURL else { return }
+                guard source.identifier == self.taskIdentifier else { return }
                 progressBlock?(receivedSize, totalSize)
-            },
+        },
             completionHandler: { result in
                 DispatchQueue.main.safeAsync {
-                    guard resource.downloadURL == self.webURL else {
+                    guard source.identifier == self.taskIdentifier else {
                         let error = KingfisherError.imageSettingError(
-                            reason: .notCurrentSource(result: result.value, error: result.error, source: .network(resource)))
+                            reason: .notCurrentSource(result: result.value, error: result.error, source: source))
                         completionHandler?(.failure(error))
                         return
                     }
-                    
+
                     self.imageTask = nil
-                    
+
                     switch result {
                     case .success(let value):
                         self.base.image = value.image
@@ -95,19 +80,13 @@ extension KingfisherClass where Base: NSButton {
                         completionHandler?(result)
                     }
                 }
-            })
-        
+        })
+
         imageTask = task
         return task
     }
-    
-    /// Cancels the image download task of the button if it is running.
-    /// Nothing will happen if the downloading has already finished.
-    public func cancelImageDownloadTask() {
-        imageTask?.cancel()
-    }
-    
-    /// Sets an alternate image to the button with a requested resource.
+
+    /// Sets an image to the button with a requested resource.
     ///
     /// - Parameters:
     ///   - resource: The `Resource` object contains information about the resource.
@@ -124,44 +103,66 @@ extension KingfisherClass where Base: NSButton {
     /// Both `progressBlock` and `completionHandler` will be also executed in the main thread.
     ///
     @discardableResult
-    public func setAlternateImage(with resource: Resource?,
-                                  placeholder: Image? = nil,
-                                  options: KingfisherOptionsInfo? = nil,
-                                  progressBlock: DownloadProgressBlock? = nil,
-                                  completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil)
-        -> DownloadTask?
+    public func setImage(
+        with resource: Resource?,
+        placeholder: Image? = nil,
+        options: KingfisherOptionsInfo? = nil,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
+    {
+        return setImage(
+            with: resource.map { .network($0) },
+            placeholder: placeholder,
+            options: options,
+            progressBlock: progressBlock,
+            completionHandler: completionHandler)
+    }
+    
+    /// Cancels the image download task of the button if it is running.
+    /// Nothing will happen if the downloading has already finished.
+    public func cancelImageDownloadTask() {
+        imageTask?.cancel()
+    }
+
+    @discardableResult
+    public func setAlternateImage(
+        with source: Source?,
+        placeholder: Image? = nil,
+        options: KingfisherOptionsInfo? = nil,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
     {
-        guard let resource = resource else {
+        guard let source = source else {
             base.alternateImage = placeholder
-            alternateWebURL = nil
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            alternateTaskIdentifier = nil
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
-        
+
         let options = KingfisherManager.shared.defaultOptions + (options ?? .empty)
         if !options.keepCurrentImageWhileLoading {
             base.alternateImage = placeholder
         }
-        
-        alternateWebURL = resource.downloadURL
+
+        alternateTaskIdentifier = source.identifier
         let task = KingfisherManager.shared.retrieveImage(
-            with: resource,
+            with: source,
             options: options,
             progressBlock: { receivedSize, totalSize in
-                guard resource.downloadURL == self.alternateWebURL else { return }
+                guard self.alternateTaskIdentifier == source.identifier else { return }
                 progressBlock?(receivedSize, totalSize)
             },
             completionHandler: { result in
-                DispatchQueue.main.safeAsync {
-                    guard resource.downloadURL == self.alternateWebURL else {
+                CallbackQueue.mainCurrentOrAsync.execute {
+                    guard self.alternateTaskIdentifier == source.identifier else {
                         let error = KingfisherError.imageSettingError(
-                            reason: .notCurrentSource(result: result.value, error: result.error, source: .network(resource)))
+                            reason: .notCurrentSource(result: result.value, error: result.error, source: source))
                         completionHandler?(.failure(error))
                         return
                     }
-                    
+
                     self.alternateImageTask = nil
-                    
+
                     switch result {
                     case .success(let value):
                         self.base.alternateImage = value.image
@@ -173,12 +174,43 @@ extension KingfisherClass where Base: NSButton {
                         completionHandler?(result)
                     }
                 }
-            })
-        
+        })
+
         alternateImageTask = task
         return task
     }
-    
+
+    /// Sets an alternate image to the button with a requested resource.
+    ///
+    /// - Parameters:
+    ///   - resource: The `Resource` object contains information about the resource.
+    ///   - placeholder: A placeholder to show while retrieving the image from the given `resource`.
+    ///   - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more.
+    ///   - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an
+    ///                    `expectedContentLength`, this block will not be called.
+    ///   - completionHandler: Called when the image retrieved and set finished.
+    /// - Returns: A task represents the image downloading.
+    ///
+    /// - Note:
+    /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache
+    /// or network. Since this method will perform UI changes, you must call it from the main thread.
+    /// Both `progressBlock` and `completionHandler` will be also executed in the main thread.
+    ///
+    @discardableResult
+    public func setAlternateImage(
+        with resource: Resource?,
+        placeholder: Image? = nil,
+        options: KingfisherOptionsInfo? = nil,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
+    {
+        return setAlternateImage(
+            with: resource.map { .network($0) },
+            placeholder: placeholder,
+            options: options,
+            progressBlock: progressBlock,
+            completionHandler: completionHandler)
+    }
  
     /// Cancels the alternate image download task of the button if it is running.
     /// Nothing will happen if the downloading has already finished.
@@ -189,34 +221,49 @@ extension KingfisherClass where Base: NSButton {
 
 
 // MARK: - Associated Object
-private var lastURLKey: Void?
+private var taskIdentifierKey: Void?
 private var imageTaskKey: Void?
 
-private var lastAlternateURLKey: Void?
+private var alternateTaskIdentifierKey: Void?
 private var alternateImageTaskKey: Void?
 
 extension KingfisherClass where Base: NSButton {
-    
-    /// Gets the image URL binded to this button.
-    public private(set) var webURL: URL? {
-        get { return getAssociatedObject(base, &lastURLKey) }
-        set { setRetainedAssociatedObject(base, &lastURLKey, newValue) }
+
+    public private(set) var taskIdentifier: String? {
+        get { return getAssociatedObject(base, &taskIdentifierKey) }
+        set { setRetainedAssociatedObject(base, &taskIdentifierKey, newValue) }
     }
     
     private var imageTask: DownloadTask? {
         get { return getAssociatedObject(base, &imageTaskKey) }
         set { setRetainedAssociatedObject(base, &imageTaskKey, newValue)}
     }
-    
-    
-    /// Gets the image URL binded to this button.
-    public private(set) var alternateWebURL: URL? {
-        get { return getAssociatedObject(base, &lastAlternateURLKey) }
-        set { setRetainedAssociatedObject(base, &lastAlternateURLKey, newValue) }
+
+    public private(set) var alternateTaskIdentifier: String? {
+        get { return getAssociatedObject(base, &alternateTaskIdentifierKey) }
+        set { setRetainedAssociatedObject(base, &alternateTaskIdentifierKey, newValue) }
     }
-    
+
     private var alternateImageTask: DownloadTask? {
         get { return getAssociatedObject(base, &alternateImageTaskKey) }
         set { setRetainedAssociatedObject(base, &alternateImageTaskKey, newValue)}
     }
 }
+
+extension KingfisherClass where Base: NSButton {
+
+    /// Gets the image URL binded to this button.
+    @available(*, deprecated, message: "Use `taskIdentifier` instead.", renamed: "taskIdentifier")
+    public private(set) var webURL: URL? {
+        get { return taskIdentifier.flatMap { URL(string: $0) } }
+        set { taskIdentifier = newValue?.absoluteString }
+    }
+
+
+    /// Gets the image URL binded to this button.
+    @available(*, deprecated, message: "Use `alternateTaskIdentifier` instead.", renamed: "alternateTaskIdentifier")
+    public private(set) var alternateWebURL: URL? {
+        get { return alternateTaskIdentifier.flatMap { URL(string: $0) } }
+        set { alternateTaskIdentifier = newValue?.absoluteString }
+    }
+}

+ 2 - 2
Sources/Extensions/UIButton+Kingfisher.swift

@@ -57,7 +57,7 @@ extension KingfisherClass where Base: UIButton {
         guard let resource = resource else {
             base.setImage(placeholder, for: state)
             setWebURL(nil, for: state)
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
         
@@ -137,7 +137,7 @@ extension KingfisherClass where Base: UIButton {
         guard let resource = resource else {
             base.setBackgroundImage(placeholder, for: state)
             setBackgroundWebURL(nil, for: state)
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
         

+ 1 - 1
Sources/Extensions/WKInterfaceImage+Kingfisher.swift

@@ -56,7 +56,7 @@ extension KingfisherClass where Base: WKInterfaceImage {
         guard let resource = resource else {
             base.setImage(placeholder)
             webURL = nil
-            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptyResource)))
+            completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
             return nil
         }
 

+ 4 - 4
Sources/General/KingfisherError.swift

@@ -155,13 +155,13 @@ public enum KingfisherError: Error {
 
     /// Represnts the error reason duting image setting in a view related class.
     ///
-    /// - emptyResource: The input resource is empty or `nil`. Code 5001.
+    /// - emptySource: The input resource is empty or `nil`. Code 5001.
     /// - notCurrentSource: The source task is finished, but it is not the one expected now. Code 5002.
     /// - dataProviderError: An error happens during getting data from an `ImageDataProvider`. Code 5003.
     public enum ImageSettingErrorReason {
         
         /// The input resource is empty or `nil`. Code 5001.
-        case emptyResource
+        case emptySource
         
         /// The resource task is finished, but it is not the one expected now. This usually happens when you set another
         /// resource on the view without cancelling the current on-going one. The previous setting task will fail with
@@ -336,7 +336,7 @@ extension KingfisherError.ProcessorErrorReason {
 extension KingfisherError.ImageSettingErrorReason {
     var errorDescription: String? {
         switch self {
-        case .emptyResource:
+        case .emptySource:
             return "The input resource is empty."
         case .notCurrentSource(let result, let error, let resource):
             if let result = result {
@@ -355,7 +355,7 @@ extension KingfisherError.ImageSettingErrorReason {
     
     var errorCode: Int {
         switch self {
-        case .emptyResource: return 5001
+        case .emptySource: return 5001
         case .notCurrentSource: return 5002
         case .dataProviderError: return 5003
         }

+ 2 - 8
Sources/General/KingfisherOptionsInfo.swift

@@ -70,14 +70,12 @@ public enum KingfisherOptionsInfoItem {
     
     /// Kingfisher will use the associated `ImageDownloader` object to download the requested images.
     case downloader(ImageDownloader)
-    
-    #if os(iOS) || os(tvOS)
+
     /// Member for animation transition when using `UIImageView`. Kingfisher will use the `ImageTransition` of
     /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the
     /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when
     /// the image being retrieved from cache, set `.forceRefresh` as well.
     case transition(ImageTransition)
-    #endif
     
     /// Associated `Float` value will be set as the priority of image download task. The value for it should be
     /// between 0.0~1.0. If this option not set, the default value (`URLSessionTask.defaultPriority`) will be used.
@@ -212,9 +210,7 @@ func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Boo
     case (.targetCache, .targetCache): return true
     case (.originalCache, .originalCache): return true
     case (.downloader, .downloader): return true
-    #if os(iOS) || os(tvOS)
     case (.transition, .transition): return true
-    #endif
     case (.downloadPriority, .downloadPriority): return true
     case (.forceRefresh, .forceRefresh): return true
     case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
@@ -282,8 +278,7 @@ public extension Collection where Iterator.Element == KingfisherOptionsInfoItem
         }
         return nil
     }
-    
-    #if os(iOS) || os(tvOS)
+
     /// Member for animation transition when using UIImageView.
     public var transition: ImageTransition {
         if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
@@ -293,7 +288,6 @@ public extension Collection where Iterator.Element == KingfisherOptionsInfoItem
         }
         return ImageTransition.none
     }
-    #endif
     
     /// A `Float` value set as the priority of image download task. The value for it should be
     /// between 0.0~1.0.

+ 5 - 0
Sources/Image/ImageTransition.swift

@@ -107,4 +107,9 @@ public enum ImageTransition {
         }
     }
 }
+#else
+// Just a placeholder for compiling on macOS.
+public enum ImageTransition {
+    case none
+}
 #endif

+ 1 - 1
Tests/KingfisherTests/ImageViewExtensionTests.swift

@@ -392,7 +392,7 @@ class ImageViewExtensionTests: XCTestCase {
         imageView.kf.setImage(with: url, progressBlock: { _, _ in XCTFail() }) {
             result in
             XCTAssertNotNil(result.error)
-            guard case .imageSettingError(reason: .emptyResource) = result.error! else {
+            guard case .imageSettingError(reason: .emptySource) = result.error! else {
                 XCTFail()
                 fatalError()
             }

+ 3 - 3
Tests/KingfisherTests/NSButtonExtensionTests.swift

@@ -76,7 +76,7 @@ class NSButtonExtensionTests: XCTestCase {
             XCTAssertNotNil(image)
             XCTAssertTrue(image!.renderEqual(to: testImage))
             XCTAssertTrue(self.button.image!.renderEqual(to: testImage))
-            XCTAssertEqual(result.value?.imageURL, self.button.kf.webURL)
+            XCTAssertEqual(result.value?.imageURL?.absoluteString, self.button.kf.taskIdentifier)
             XCTAssertEqual(result.value!.cacheType, .none)
             
             exp.fulfill()
@@ -98,7 +98,7 @@ class NSButtonExtensionTests: XCTestCase {
             XCTAssertNotNil(image)
             XCTAssertTrue(image!.renderEqual(to: testImage))
             XCTAssertTrue(self.button.alternateImage!.renderEqual(to: testImage))
-            XCTAssertEqual(result.value?.imageURL, self.button.kf.alternateWebURL)
+            XCTAssertEqual(result.value?.imageURL?.absoluteString, self.button.kf.alternateTaskIdentifier)
             XCTAssertEqual(result.value!.cacheType, .none)
             
             exp.fulfill()
@@ -149,7 +149,7 @@ class NSButtonExtensionTests: XCTestCase {
             XCTAssertNil(result.value)
             XCTAssertNotNil(result.error)
             
-            guard case .imageSettingError(reason: .emptyResource) = result.error! else {
+            guard case .imageSettingError(reason: .emptySource) = result.error! else {
                 XCTFail()
                 fatalError()
             }

+ 1 - 1
Tests/KingfisherTests/UIButtonExtensionTests.swift

@@ -152,7 +152,7 @@ class UIButtonExtensionTests: XCTestCase {
         button.kf.setBackgroundImage(with: url, for: .normal) { result in
             XCTAssertNil(result.value)
             XCTAssertNotNil(result.error)
-            guard case .imageSettingError(reason: .emptyResource) = result.error! else {
+            guard case .imageSettingError(reason: .emptySource) = result.error! else {
                 XCTFail()
                 return
             }