Просмотр исходного кода

Add test scene for loading images in list

onevcat 6 лет назад
Родитель
Сommit
fdaf2cbc07

+ 1 - 1
Demo/Demo/Kingfisher-Demo/ViewControllers/MainViewController.swift

@@ -41,7 +41,7 @@ class MainViewController: UITableViewController {
         if indexPath.row == 9 {
         if indexPath.row == 9 {
 
 
             if #available(iOS 13.0, *) {
             if #available(iOS 13.0, *) {
-                navigationController?.pushViewController(UIHostingController(rootView: SwiftUIView()), animated: true)
+                navigationController?.pushViewController(UIHostingController(rootView: SwiftUIMainScreen()), animated: true)
             } else {
             } else {
                 print("Not supported on this deploy platform.")
                 print("Not supported on this deploy platform.")
             }
             }

+ 16 - 15
Demo/Demo/Kingfisher-Demo/ViewControllers/SwiftUIView.swift → Demo/Demo/Kingfisher-Demo/ViewControllers/SwiftUIScreens/SwiftUIList.swift

@@ -1,5 +1,5 @@
 //
 //
-//  SwiftUIView.swift
+//  SwiftUIList.swift
 //  Kingfisher
 //  Kingfisher
 //
 //
 //  Created by Wei Wang on 2019/06/18.
 //  Created by Wei Wang on 2019/06/18.
@@ -28,30 +28,31 @@ import Kingfisher
 import SwiftUI
 import SwiftUI
 
 
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
-struct SwiftUIView : SwiftUI.View {
-    var body: some SwiftUI.View {
-        VStack {
-            Text("Hello World")
-            KFImage(url: URL(string: "https://onevcat.com/assets/images/avatar.jpg")!)
+struct SwiftUIList : View {
+
+    let index = Array(repeating: 1...10, count: 10).flatMap { $0 }
+
+    var body: some View {
+        List(index) { i in
+            KFImage(url: URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/Loading/kingfisher-\(i).jpg")!)
                 .resizable()
                 .resizable()
                 .onSuccess { r in
                 .onSuccess { r in
-                    print(r)
+                    print("suc: \(i)")
                 }
                 }
                 .onFailure { e in
                 .onFailure { e in
-                    print("Get an error \(e)")
+                    print("err: \(i)")
                 }
                 }
-                .padding()
-                .background(Color.red)
-                .frame(width: 400, height: 400)
-            Text("Test Me")
-        }
+                .placeholder(image: Image(systemName: "star.fill"))
+                .frame(width: 300, height: 300)
+                .cornerRadius(20)
+        }.navigationBarTitle(Text("Basic Image"))
     }
     }
 }
 }
 
 
 #if DEBUG
 #if DEBUG
-struct SwiftUIView_Previews : PreviewProvider {
+struct SwiftUIList_Previews : PreviewProvider {
     static var previews: some View {
     static var previews: some View {
-        SwiftUIView()
+        SwiftUIList()
     }
     }
 }
 }
 #endif
 #endif

+ 47 - 0
Demo/Demo/Kingfisher-Demo/ViewControllers/SwiftUIScreens/SwiftUIMainScreen.swift

@@ -0,0 +1,47 @@
+//
+//  SwiftUIMainScreen.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2019/06/27.
+//
+//  Copyright (c) 2019 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.
+
+import Kingfisher
+import SwiftUI
+
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+struct SwiftUIMainScreen : SwiftUI.View {
+
+    var body: some SwiftUI.View {
+        List {
+            NavigationButton(destination: SwiftUIView()) { Text("Basic Image") }
+            NavigationButton(destination: SwiftUIList()) { Text("List") }
+        }
+    }
+}
+
+#if DEBUG
+struct SwiftUIMainScreen_Previews : PreviewProvider {
+    static var previews: some View {
+        SwiftUIMainScreen()
+    }
+}
+#endif

+ 66 - 0
Demo/Demo/Kingfisher-Demo/ViewControllers/SwiftUIScreens/SwiftUIView.swift

@@ -0,0 +1,66 @@
+//
+//  SwiftUIView.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2019/06/18.
+//
+//  Copyright (c) 2019 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.
+
+import Kingfisher
+import SwiftUI
+
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+struct SwiftUIView : View {
+
+    @State private var index = "1"
+    @State private var result = ""
+
+    var body: some View {
+        VStack {
+            KFImage(url: URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/Loading/kingfisher-\(self.index).jpg")!)
+                .resizable()
+                .onSuccess { r in
+                    print("suc: \(r)")
+                    self.result = "\(r.cacheType)"
+                }
+                .onFailure { e in
+                    print("err: \(e)")
+                    self.result = "\(e)"
+                }
+                .placeholder(image: Image(systemName: "star.fill"))
+                .frame(width: 300, height: 300)
+                .cornerRadius(20)
+
+            Button(action: {
+                self.index = String(Int(self.index)! + 1)
+            }) { Text("+1") }
+
+        }.navigationBarTitle(Text("Basic Image"))
+    }
+}
+
+#if DEBUG
+struct SwiftUIView_Previews : PreviewProvider {
+    static var previews: some View {
+        SwiftUIView()
+    }
+}
+#endif

+ 21 - 3
Demo/Kingfisher-Demo.xcodeproj/project.pbxproj

@@ -34,6 +34,7 @@
 		D12E0CA31C47F92200AC98AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D12E0C9E1C47F92200AC98AD /* Assets.xcassets */; };
 		D12E0CA31C47F92200AC98AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D12E0C9E1C47F92200AC98AD /* Assets.xcassets */; };
 		D12E0CA41C47F92200AC98AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D12E0C9F1C47F92200AC98AD /* Main.storyboard */; };
 		D12E0CA41C47F92200AC98AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D12E0C9F1C47F92200AC98AD /* Main.storyboard */; };
 		D12E0CB61C47F9C100AC98AD /* NormalLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12E0C941C47F91800AC98AD /* NormalLoadingViewController.swift */; };
 		D12E0CB61C47F9C100AC98AD /* NormalLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12E0C941C47F91800AC98AD /* NormalLoadingViewController.swift */; };
+		D139767D22C501D40073D00D /* SwiftUIMainScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D139767C22C501D40073D00D /* SwiftUIMainScreen.swift */; };
 		D1679A461C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D1679A451C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		D1679A461C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D1679A451C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		D1679A531C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = D1679A391C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app */; };
 		D1679A531C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = D1679A391C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app */; };
 		D17176322047826A00EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17176332047826A00EFC8C5 /* Kingfisher.framework */; };
 		D17176322047826A00EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17176332047826A00EFC8C5 /* Kingfisher.framework */; };
@@ -43,6 +44,7 @@
 		D1717638204782D500EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1717639204782D500EFC8C5 /* Kingfisher.framework */; };
 		D1717638204782D500EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1717639204782D500EFC8C5 /* Kingfisher.framework */; };
 		D171763E204782F300EFC8C5 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D1717639204782D500EFC8C5 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		D171763E204782F300EFC8C5 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D1717639204782D500EFC8C5 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		D171763F2047837900EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17176402047837900EFC8C5 /* Kingfisher.framework */; };
 		D171763F2047837900EFC8C5 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17176402047837900EFC8C5 /* Kingfisher.framework */; };
+		D17678CF22C517E500972227 /* SwiftUIList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D17678CE22C517E400972227 /* SwiftUIList.swift */; };
 		D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA221A1879600263AD8 /* MainViewController.swift */; };
 		D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA221A1879600263AD8 /* MainViewController.swift */; };
 		D1A1CCA721A18A3200263AD8 /* UIViewController+KingfisherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */; };
 		D1A1CCA721A18A3200263AD8 /* UIViewController+KingfisherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */; };
 		D1A1CCA821A18A3200263AD8 /* UIViewController+KingfisherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */; };
 		D1A1CCA821A18A3200263AD8 /* UIViewController+KingfisherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */; };
@@ -163,6 +165,7 @@
 		D12E0C9E1C47F92200AC98AD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		D12E0C9E1C47F92200AC98AD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		D12E0CA01C47F92200AC98AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		D12E0CA01C47F92200AC98AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		D12E0CA11C47F92200AC98AD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		D12E0CA11C47F92200AC98AD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D139767C22C501D40073D00D /* SwiftUIMainScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIMainScreen.swift; sourceTree = "<group>"; };
 		D13F49C21BEDA53F00CE335D /* Kingfisher-tvOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Kingfisher-tvOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D13F49C21BEDA53F00CE335D /* Kingfisher-tvOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Kingfisher-tvOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1679A391C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Kingfisher-watchOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1679A391C4E78B20020FD12 /* Kingfisher-watchOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Kingfisher-watchOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1679A451C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Kingfisher-watchOS-Demo Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1679A451C4E78B20020FD12 /* Kingfisher-watchOS-Demo Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Kingfisher-watchOS-Demo Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -170,6 +173,7 @@
 		D1717636204782AE00EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1717636204782AE00EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1717639204782D500EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1717639204782D500EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D17176402047837900EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D17176402047837900EFC8C5 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D17678CE22C517E400972227 /* SwiftUIList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIList.swift; sourceTree = "<group>"; };
 		D1A1CCA221A1879600263AD8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
 		D1A1CCA221A1879600263AD8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
 		D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+KingfisherOperation.swift"; sourceTree = "<group>"; };
 		D1A1CCA621A18A3200263AD8 /* UIViewController+KingfisherOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+KingfisherOperation.swift"; sourceTree = "<group>"; };
 		D1CE1BCF21A1AFA300419000 /* TransitionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionViewController.swift; sourceTree = "<group>"; };
 		D1CE1BCF21A1AFA300419000 /* TransitionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionViewController.swift; sourceTree = "<group>"; };
@@ -313,6 +317,16 @@
 			path = "Demo/Kingfisher-tvOS-Demo";
 			path = "Demo/Kingfisher-tvOS-Demo";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		D139767B22C501AD0073D00D /* SwiftUIScreens */ = {
+			isa = PBXGroup;
+			children = (
+				D139767C22C501D40073D00D /* SwiftUIMainScreen.swift */,
+				D17678CE22C517E400972227 /* SwiftUIList.swift */,
+				4BB3DE8522B8D9C400F65D51 /* SwiftUIView.swift */,
+			);
+			path = SwiftUIScreens;
+			sourceTree = "<group>";
+		};
 		D1A1CCA521A1895000263AD8 /* Extensions */ = {
 		D1A1CCA521A1895000263AD8 /* Extensions */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -324,7 +338,7 @@
 		D1A1CCA921A1936300263AD8 /* ViewControllers */ = {
 		D1A1CCA921A1936300263AD8 /* ViewControllers */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				4BB3DE8522B8D9C400F65D51 /* SwiftUIView.swift */,
+				D139767B22C501AD0073D00D /* SwiftUIScreens */,
 				D1A1CCA221A1879600263AD8 /* MainViewController.swift */,
 				D1A1CCA221A1879600263AD8 /* MainViewController.swift */,
 				D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */,
 				D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */,
 				4B1C7A3C21A256E300CE9D31 /* InfinityCollectionViewController.swift */,
 				4B1C7A3C21A256E300CE9D31 /* InfinityCollectionViewController.swift */,
@@ -618,6 +632,7 @@
 				4BB3DE8622B8D9C400F65D51 /* SwiftUIView.swift in Sources */,
 				4BB3DE8622B8D9C400F65D51 /* SwiftUIView.swift in Sources */,
 				D1CE1BD321A1B45A00419000 /* ImageLoader.swift in Sources */,
 				D1CE1BD321A1B45A00419000 /* ImageLoader.swift in Sources */,
 				D12E0C9B1C47F91800AC98AD /* NormalLoadingViewController.swift in Sources */,
 				D12E0C9B1C47F91800AC98AD /* NormalLoadingViewController.swift in Sources */,
+				D17678CF22C517E500972227 /* SwiftUIList.swift in Sources */,
 				D1CE1BD021A1AFA300419000 /* TransitionViewController.swift in Sources */,
 				D1CE1BD021A1AFA300419000 /* TransitionViewController.swift in Sources */,
 				D10AC99821A300C9005F057C /* ProcessorCollectionViewController.swift in Sources */,
 				D10AC99821A300C9005F057C /* ProcessorCollectionViewController.swift in Sources */,
 				D1F06F3921AAF1EE000B1C38 /* IndicatorCollectionViewController.swift in Sources */,
 				D1F06F3921AAF1EE000B1C38 /* IndicatorCollectionViewController.swift in Sources */,
@@ -629,6 +644,7 @@
 				D1F06F3321AA4292000B1C38 /* DetailImageViewController.swift in Sources */,
 				D1F06F3321AA4292000B1C38 /* DetailImageViewController.swift in Sources */,
 				4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */,
 				4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */,
 				D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */,
 				D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */,
+				D139767D22C501D40073D00D /* SwiftUIMainScreen.swift in Sources */,
 				D1F06F3721AAEACF000B1C38 /* GIFViewController.swift in Sources */,
 				D1F06F3721AAEACF000B1C38 /* GIFViewController.swift in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -906,8 +922,9 @@
 				MTL_ENABLE_DEBUG_INFO = YES;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				TVOS_DEPLOYMENT_TARGET = 10.0;
 				TVOS_DEPLOYMENT_TARGET = 10.0;
 				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 				WATCHOS_DEPLOYMENT_TARGET = 3.0;
@@ -958,8 +975,9 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.12;
 				MACOSX_DEPLOYMENT_TARGET = 10.12;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
-				SWIFT_VERSION = 4.2;
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				TARGETED_DEVICE_FAMILY = "1,2";
 				TVOS_DEPLOYMENT_TARGET = 10.0;
 				TVOS_DEPLOYMENT_TARGET = 10.0;
 				VALIDATE_PRODUCT = YES;
 				VALIDATE_PRODUCT = YES;

+ 9 - 3
Sources/SwiftUI/ImageBinder.swift

@@ -29,14 +29,16 @@ import SwiftUI
 
 
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 extension KFImage {
 extension KFImage {
-    class ImageBinder: BindableObject {
+    public class ImageBinder: BindableObject {
         let url: URL
         let url: URL
 
 
-        var didChange = PassthroughSubject<KFCrossPlatformImage?, Never>()
+        public var didChange = PassthroughSubject<KFCrossPlatformImage?, Never>()
         var subject = PassthroughSubject<RetrieveImageResult, KingfisherError>()
         var subject = PassthroughSubject<RetrieveImageResult, KingfisherError>()
 
 
         var subscriber: Subscribers.Sink<PassthroughSubject<RetrieveImageResult, KingfisherError>>?
         var subscriber: Subscribers.Sink<PassthroughSubject<RetrieveImageResult, KingfisherError>>?
 
 
+        var downloadTask: DownloadTask?
+
         var image: Kingfisher.KFCrossPlatformImage? {
         var image: Kingfisher.KFCrossPlatformImage? {
             didSet {
             didSet {
                 didChange.send(image)
                 didChange.send(image)
@@ -48,7 +50,7 @@ extension KFImage {
         }
         }
 
 
         func start() {
         func start() {
-            _ = KingfisherManager.shared.retrieveImage(with: .network(url)) { r in
+            downloadTask = KingfisherManager.shared.retrieveImage(with: .network(url)) { r in
                 switch r {
                 switch r {
                 case .success(let result):
                 case .success(let result):
                     self.image = result.image
                     self.image = result.image
@@ -59,6 +61,10 @@ extension KFImage {
                 }
                 }
             }
             }
         }
         }
+
+        public func cancel() {
+            downloadTask?.cancel()
+        }
     }
     }
 
 
 }
 }

+ 32 - 10
Sources/SwiftUI/KFImage.swift

@@ -27,14 +27,25 @@
 import SwiftUI
 import SwiftUI
 import Combine
 import Combine
 
 
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+extension Image {
+    init(crossPlatformImage: KFCrossPlatformImage) {
+        #if canImport(UIKit)
+        self.init(uiImage: crossPlatformImage)
+        #elseif canImport(AppKit)
+        self.init(nsImage: crossPlatformImage)
+        #endif
+    }
+}
+
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 public struct KFImage: View {
 public struct KFImage: View {
 
 
-    static let empty = Kingfisher.KFCrossPlatformImage()
+    var placeholder = Image(crossPlatformImage: .init())
 
 
     var configs: [(Image) -> Image]
     var configs: [(Image) -> Image]
 
 
-    @ObjectBinding var binder: ImageBinder
+    @ObjectBinding public private(set) var binder: ImageBinder
 
 
     private let onFailureDelegate = Delegate<KingfisherError, Void>()
     private let onFailureDelegate = Delegate<KingfisherError, Void>()
     private let onSuccessDelegate = Delegate<RetrieveImageResult, Void>()
     private let onSuccessDelegate = Delegate<RetrieveImageResult, Void>()
@@ -45,15 +56,12 @@ public struct KFImage: View {
     }
     }
 
 
     public var body: some View {
     public var body: some View {
-        #if canImport(UIKit)
-        let image = Image(uiImage: binder.image ?? KFImage.empty)
-        #elseif canImport(AppKit)
-        let image = Image(nsImage: binder.image ?? KFImage.empty)
-        #endif
-
+        print("Loading body \(binder.url)")
+        let image = binder.image.map { Image(crossPlatformImage: $0) } ?? placeholder
         return configs
         return configs
             .reduce(image) { current, config in config(current) }
             .reduce(image) { current, config in config(current) }
             .onAppear { [unowned binder] in
             .onAppear { [unowned binder] in
+                binder.subscriber?.cancel()
                 binder.subscriber = binder.subject.sink(
                 binder.subscriber = binder.subject.sink(
                     receiveCompletion: { complete in
                     receiveCompletion: { complete in
                         switch complete {
                         switch complete {
@@ -68,6 +76,7 @@ public struct KFImage: View {
                         self.onSuccessDelegate.call(result)
                         self.onSuccessDelegate.call(result)
                     }
                     }
                 )
                 )
+                print("Start!! \(binder.url)")
                 binder.start()
                 binder.start()
             }
             }
     }
     }
@@ -100,6 +109,20 @@ extension KFImage {
     public func antialiased(_ isAntialiased: Bool) -> KFImage {
     public func antialiased(_ isAntialiased: Bool) -> KFImage {
         config { $0.antialiased(isAntialiased) }
         config { $0.antialiased(isAntialiased) }
     }
     }
+
+    public func placeholder(image: Image?) -> KFImage {
+        var result = self
+        result.placeholder = image ?? Image(crossPlatformImage: .init())
+        return result
+    }
+
+    public func placeholder(name: String, bundle: Bundle? = nil) -> KFImage {
+        return placeholder(image: .init(name, bundle: bundle))
+    }
+
+    public func placeholder(systemName: String) -> KFImage {
+        return placeholder(image: .init(systemName: systemName))
+    }
 }
 }
 
 
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@@ -124,11 +147,10 @@ extension KFImage {
 struct KFImage_Previews : PreviewProvider {
 struct KFImage_Previews : PreviewProvider {
     static var previews: some View {
     static var previews: some View {
         KFImage(url:URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!)
         KFImage(url:URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!)
-        .resizable()
         .onSuccess { r in
         .onSuccess { r in
             print(r)
             print(r)
         }
         }
-        .interpolation(.medium)
+        .resizable()
         .aspectRatio(contentMode: .fit)
         .aspectRatio(contentMode: .fit)
         .padding()
         .padding()
     }
     }