Переглянути джерело

Support fade and force transition for KFImage

onevcat 5 роки тому
батько
коміт
1f67547778

+ 1 - 0
Demo/Demo/Kingfisher-Demo/SwiftUIViews/SwiftUIList.swift

@@ -75,6 +75,7 @@ struct SwiftUIList : View {
                         }
                         .foregroundColor(.gray)
                     }
+                    .fade(duration: 1)
                     .cancelOnDisappear(true)
                     .aspectRatio(contentMode: .fit)
                     .cornerRadius(20)

+ 2 - 0
Demo/Demo/Kingfisher-Demo/SwiftUIViews/SwiftUIView.swift

@@ -53,6 +53,8 @@ struct SwiftUIView : View {
                     Image(systemName: "arrow.2.circlepath.circle")
                         .font(.largeTitle)
                 }
+                .fade(duration: 1)
+                .forceTransition()
                 .resizable()
                 .frame(width: 300, height: 300)
                 .cornerRadius(20)

+ 4 - 1
Sources/Extensions/ImageView+Kingfisher.swift

@@ -383,7 +383,10 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView {
         switch options.transition {
         case .none:
             return false
-        #if !os(macOS)
+        #if os(macOS)
+        case .fade: // Fade is only a placeholder for SwiftUI on macOS.
+            return false
+        #else
         default:
             if options.forceTransition { return true }
             if cacheType == .none { return true }

+ 0 - 8
Sources/General/KF.swift

@@ -333,14 +333,6 @@ extension KF.Builder {
     }
     #endif
 
-    /// Sets whether the image setting for an image view should happen with transition even when retrieved from cache.
-    /// - Parameter enabled: Enable the force transition or not.
-    /// - Returns: A `KF.Builder` with changes applied.
-    public func forceTransition(_ enabled: Bool = true) -> Self {
-        options.forceTransition = enabled
-        return self
-    }
-
     /// Sets whether keeping the existing image of image view while setting another image to it.
     /// - Parameter enabled: Whether the existing image should be kept.
     /// - Returns: A `KF.Builder` with changes applied.

+ 9 - 0
Sources/General/KFOptionsSetter.swift

@@ -339,6 +339,15 @@ extension KFOptionSetter {
         options.lowDataModeSource = source
         return self
     }
+
+    /// Sets whether the image setting for an image view should happen with transition even when retrieved from cache.
+    /// - Parameter enabled: Enable the force transition or not.
+    /// - Returns: A `KF.Builder` with changes applied.
+    public func forceTransition(_ enabled: Bool = true) -> Self {
+        options.forceTransition = enabled
+        return self
+    }
+
 }
 
 // MARK: - Request Modifier

+ 3 - 0
Sources/Image/ImageTransition.swift

@@ -24,6 +24,7 @@
 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 //  THE SOFTWARE.
 
+import Foundation
 #if os(iOS) || os(tvOS)
 import UIKit
 
@@ -111,5 +112,7 @@ public enum ImageTransition {
 // Just a placeholder for compiling on macOS.
 public enum ImageTransition {
     case none
+    /// This is a placeholder on macOS now. It is for SwiftUI (KFImage) to identify the fade option only.
+    case fade(TimeInterval)
 }
 #endif

+ 33 - 3
Sources/SwiftUI/KFImage.swift

@@ -156,7 +156,6 @@ struct KFImageRenderer: View {
                     current, config in config(current)
                 }
                 .opacity(isLoaded ? 1.0 : 0.0)
-                .animation(.default)
         } else {
             Group {
                 if placeholder != nil {
@@ -172,8 +171,19 @@ struct KFImageRenderer: View {
                 if !binder.loadingOrSucceeded {
                     binder.start {
                         self.loadingResult = $0
-                        loadingResult?.matchSuccess { _ in
-                            CallbackQueue.mainAsync.execute { isLoaded = true }
+                        switch $0 {
+                        case .success(let result):
+                            CallbackQueue.mainAsync.execute {
+                                if let duration = fadeTransitionDuration(cacheType: result.cacheType) {
+                                    withAnimation(.linear(duration: duration)) {
+                                        isLoaded = true
+                                    }
+                                } else {
+                                    isLoaded = true
+                                }
+                            }
+                        case .failure(_):
+                            break
                         }
                     }
                 }
@@ -188,6 +198,26 @@ struct KFImageRenderer: View {
             }
         }
     }
+
+    private func fadeTransitionDuration(cacheType: CacheType) -> TimeInterval? {
+        if binder.options.forceTransition || cacheType == .none {
+            return binder.options.transition.fadeDuration
+        } else {
+            return nil
+        }
+    }
+}
+
+extension ImageTransition {
+    // Only for fade effect in SwiftUI.
+    var fadeDuration: TimeInterval? {
+        switch self {
+        case .fade(let duration):
+            return duration
+        default:
+            return nil
+        }
+    }
 }
 
 // MARK: - Image compatibility.

+ 13 - 0
Sources/SwiftUI/KFImageOptions.swift

@@ -125,5 +125,18 @@ extension KFImage {
         result.context.cancelOnDisappear = flag
         return result
     }
+
+    /// Sets a fade transition for the image task.
+    /// - Parameter duration: The duration of the fade transition.
+    /// - Returns: A `KFImage` with changes applied.
+    ///
+    /// Kingfisher will use the fade transition 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, also call `forceRefresh()` on the returned `KFImage`.
+    public func fade(duration: TimeInterval) -> KFImage {
+        context.binder.options.transition = .fade(duration)
+        return self
+    }
 }
 #endif