浏览代码

Separate KFImage declaration and renderer

onevcat 5 年之前
父节点
当前提交
6b9e2c1eac
共有 3 个文件被更改,包括 69 次插入33 次删除
  1. 6 6
      Sources/General/KFOptionsSetter.swift
  2. 61 25
      Sources/SwiftUI/KFImage.swift
  3. 2 2
      Sources/SwiftUI/KFImageOptions.swift

+ 6 - 6
Sources/General/KFOptionsSetter.swift

@@ -45,15 +45,15 @@ extension KF.Builder: KFOptionSetter {
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 extension KFImage: KFOptionSetter {
     public var options: KingfisherParsedOptionsInfo {
-        get { binder.options }
-        nonmutating set { binder.options = newValue }
+        get { context.binder.options }
+        nonmutating set { context.binder.options = newValue }
     }
 
-    public var onFailureDelegate: Delegate<KingfisherError, Void> { binder.onFailureDelegate }
-    public var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { binder.onSuccessDelegate }
-    public var onProgressDelegate: Delegate<(Int64, Int64), Void> { binder.onProgressDelegate }
+    public var onFailureDelegate: Delegate<KingfisherError, Void> { context.binder.onFailureDelegate }
+    public var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { context.binder.onSuccessDelegate }
+    public var onProgressDelegate: Delegate<(Int64, Int64), Void> { context.binder.onProgressDelegate }
 
-    public var delegateObserver: AnyObject { binder }
+    public var delegateObserver: AnyObject { context.binder }
 }
 #endif
 

+ 61 - 25
Sources/SwiftUI/KFImage.swift

@@ -40,25 +40,10 @@ extension Image {
     }
 }
 
-/// A Kingfisher compatible SwiftUI `View` to load an image from a `Source`.
-/// Declaring a `KFImage` in a `View`'s body to trigger loading from the given `Source`.
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 public struct KFImage: View {
 
-    // TODO: Replace `@ObservedObject` with `@StateObject` once we do not need to support iOS 13.
-    /// An image binder that manages loading and cancelling image related task.
-    @ObservedObject private(set) var binder: ImageBinder
-
-    @State private var loadingResult: Result<RetrieveImageResult, KingfisherError>?
-
-    // Acts as a placeholder when loading an image.
-    var placeholder: AnyView?
-
-    // Whether the download task should be cancelled when the view disappears.
-    var cancelOnDisappear: Bool = false
-
-    // Configurations should be performed on the image.
-    var configurations: [(Image) -> Image]
+    var context: Context
 
     /// Creates a Kingfisher compatible image view to load image from the given `Source`.
     /// - Parameter source: The image `Source` defining where to load the target image.
@@ -72,9 +57,8 @@ public struct KFImage: View {
     ///               for more.
     @available(*, deprecated, message: "Some options are not available in SwiftUI yet. Use `KFImage(source:isLoaded:)` to create a `KFImage` and configure the options through modifier instead.")
     public init(source: Source?, options: KingfisherOptionsInfo? = nil, isLoaded: Binding<Bool> = .constant(false)) {
-        let binder = ImageBinder(source: source, options: options, isLoaded: isLoaded)
-        self.binder = binder
-        configurations = []
+        let binder = KFImage.ImageBinder(source: source, options: options, isLoaded: isLoaded)
+        self.init(binder: binder)
     }
 
     /// Creates a Kingfisher compatible image view to load image from the given `URL`.
@@ -88,10 +72,12 @@ public struct KFImage: View {
     ///               `KFImage` and configure the options through modifier instead. See methods of `KFOptionSetter`
     ///               for more.
     @available(*, deprecated, message: "Some options are not available in SwiftUI yet. Use `KFImage(_:isLoaded:)` to create a `KFImage` and configure the options through modifier instead.")
-    public init(_ url: URL?, options: KingfisherOptionsInfo? = nil, isLoaded: Binding<Bool> = .constant(false)) {
+    init(_ url: URL?, options: KingfisherOptionsInfo? = nil, isLoaded: Binding<Bool> = .constant(false)) {
         self.init(source: url?.convertToSource(), options: options, isLoaded: isLoaded)
     }
 
+
+
     /// Creates a Kingfisher compatible image view to load image from the given `Source`.
     /// - Parameters:
     ///   - source: The image `Source` defining where to load the target image.
@@ -100,10 +86,10 @@ public struct KFImage: View {
     ///               wrapped value from outside.
     public init(source: Source?, isLoaded: Binding<Bool> = .constant(false)) {
         let binder = ImageBinder(source: source, isLoaded: isLoaded)
-        self.binder = binder
-        configurations = []
+        self.init(binder: binder)
     }
 
+
     /// Creates a Kingfisher compatible image view to load image from the given `URL`.
     /// - Parameters:
     ///   - source: The image `Source` defining where to load the target image.
@@ -114,9 +100,59 @@ public struct KFImage: View {
         self.init(source: url?.convertToSource(), isLoaded: isLoaded)
     }
 
-    /// Declares the content and behavior of this view.
+    init(binder: ImageBinder) {
+        self.context = Context(binder: binder)
+    }
+
     public var body: some View {
+        KFImageRenderer(context)
+            .id(context.binder.source?.url?.absoluteString ?? "-1")
+    }
+}
+
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+extension KFImage {
+    struct Context {
+        var binder: ImageBinder
+        var configurations: [(Image) -> Image] = []
+        var cancelOnDisappear: Bool = false
+        var placeholder: AnyView? = nil
+
+        init(binder: ImageBinder) {
+            self.binder = binder
+        }
+    }
+}
+
+/// A Kingfisher compatible SwiftUI `View` to load an image from a `Source`.
+/// Declaring a `KFImage` in a `View`'s body to trigger loading from the given `Source`.
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+struct KFImageRenderer: View {
+
+    // TODO: Replace `@ObservedObject` with `@StateObject` once we do not need to support iOS 13.
+    /// An image binder that manages loading and cancelling image related task.
+    @ObservedObject private(set) var binder: KFImage.ImageBinder
 
+    @State private var loadingResult: Result<RetrieveImageResult, KingfisherError>?
+
+    // Acts as a placeholder when loading an image.
+    var placeholder: AnyView?
+
+    // Whether the download task should be cancelled when the view disappears.
+    let cancelOnDisappear: Bool
+
+    // Configurations should be performed on the image.
+    let configurations: [(Image) -> Image]
+
+    init(_ context: KFImage.Context) {
+        self.binder = context.binder
+        self.configurations = context.configurations
+        self.placeholder = context.placeholder
+        self.cancelOnDisappear = context.cancelOnDisappear
+    }
+
+    /// Declares the content and behavior of this view.
+    var body: some View {
         if case .success(let r) = loadingResult {
             configurations
                 .reduce(Image(crossPlatformImage: r.image)) {
@@ -159,7 +195,7 @@ extension KFImage {
     /// - Returns: A `KFImage` view that configures internal `Image` with `block`.
     public func configure(_ block: @escaping (Image) -> Image) -> KFImage {
         var result = self
-        result.configurations.append(block)
+        result.context.configurations.append(block)
         return result
     }
 
@@ -188,7 +224,7 @@ extension KFImage {
 struct KFImage_Previews : PreviewProvider {
     static var previews: some View {
         Group {
-            KFImage(URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!)
+            KFImage(source: .network(URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!))
                 .onSuccess { r in
                     print(r)
                 }

+ 2 - 2
Sources/SwiftUI/KFImageOptions.swift

@@ -113,7 +113,7 @@ extension KFImage {
     public func placeholder<Content: View>(@ViewBuilder _ content: () -> Content) -> KFImage {
         let v = content()
         var result = self
-        result.placeholder = AnyView(v)
+        result.context.placeholder = AnyView(v)
         return result
     }
 
@@ -122,7 +122,7 @@ extension KFImage {
     /// - Returns: A `KFImage` view that cancels downloading task when disappears.
     public func cancelOnDisappear(_ flag: Bool) -> KFImage {
         var result = self
-        result.cancelOnDisappear = flag
+        result.context.cancelOnDisappear = flag
         return result
     }
 }