Răsfoiți Sursa

Implement `contentConfiguration` for generic typed view

onevcat 3 ani în urmă
părinte
comite
53f26d294e

+ 1 - 0
Sources/SwiftUI/ImageContext.swift

@@ -38,6 +38,7 @@ extension KFImage {
 
         var configurations: [(HoldingView) -> HoldingView] = []
         var renderConfigurations: [(HoldingView.RenderingView) -> Void] = []
+        var contentConfiguration: ((HoldingView) -> AnyView)? = nil
         
         var cancelOnDisappear: Bool = false
         var placeholder: ((Progress) -> AnyView)? = nil

+ 21 - 2
Sources/SwiftUI/KFImageProtocol.swift

@@ -60,13 +60,32 @@ extension KFImageProtocol {
         self.init(source: url?.convertToSource())
     }
     
-    /// Configures current image with a `block`. This block will be lazily applied when creating the final `Image`.
-    /// - Parameter block: The block applies to loaded image.
+    /// Configures current image with a `block` and return another `Image` to use as the final content.
+    ///
+    /// This block will be lazily applied when creating the final `Image`.
+    ///
+    /// If multiple `configure` modifiers are added to the image, they will be evaluated by order. If you want to
+    /// configure the input image (which is usually an `Image` value) to a non-`Image` value, use `contentConfigure`.
+    ///
+    /// - Parameter block: The block applies to loaded image. The block should return an `Image` that is configured.
     /// - Returns: A `KFImage` view that configures internal `Image` with `block`.
     public func configure(_ block: @escaping (HoldingView) -> HoldingView) -> Self {
         context.configurations.append(block)
         return self
     }
+
+    /// Configures current image with a `block` and return a `View` to use as the final content.
+    ///
+    /// This block will be lazily applied when creating the final `Image`.
+    ///
+    /// If multiple `contentConfigure` modifiers are added to the image, only the last one will be stored and used.
+    ///
+    /// - Parameter block: The block applies to the loaded image. The block should return a `View` that is configured.
+    /// - Returns: A `KFImage` view that configures internal `Image` with `block`.
+    public func contentConfigure<V: View>(_ block: @escaping (HoldingView) -> V) -> Self {
+        context.contentConfiguration = { AnyView(block($0)) }
+        return self
+    }
 }
 
 @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)

+ 14 - 5
Sources/SwiftUI/KFImageRenderer.swift

@@ -42,11 +42,7 @@ struct KFImageRenderer<HoldingView> : View where HoldingView: KFImageHoldingView
         }
         
         return ZStack {
-            context.configurations
-                .reduce(HoldingView.created(from: binder.loadedImage, context: context)) {
-                    current, config in config(current)
-                }
-                .opacity(binder.loaded ? 1.0 : 0.0)
+            renderedImage().opacity(binder.loaded ? 1.0 : 0.0)
             if binder.loadedImage == nil {
                 ZStack {
                     if let placeholder = context.placeholder, let view = placeholder(binder.progress) {
@@ -84,6 +80,19 @@ struct KFImageRenderer<HoldingView> : View where HoldingView: KFImageHoldingView
         // It should be a bug in iOS 16, I guess it is some kinds of over-optimization in list cell loading caused it.
         .onAppear()
     }
+    
+    @ViewBuilder
+    private func renderedImage() -> some View {
+        let configuredImage = context.configurations
+            .reduce(HoldingView.created(from: binder.loadedImage, context: context)) {
+                current, config in config(current)
+            }
+        if let contentConfiguration = context.contentConfiguration {
+            contentConfiguration(configuredImage)
+        } else {
+            configuredImage
+        }
+    }
 }
 
 @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)