From 417f0fd6a5190b5df8b77c9ee9341d92c8fced0c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Nov 2019 03:05:27 +0800 Subject: [PATCH 1/3] Fix the indicator will create multiple times, only create once with binding --- SDWebImageSwiftUI/Classes/Indicator/Indicator.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 3dc0b5fe..5db7e6ed 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -29,7 +29,7 @@ public struct Indicator where T : View { struct IndicatorViewModifier : ViewModifier where T : View { @ObservedObject var imageManager: ImageManager - var indicator: Indicator + let indicatorView: T func body(content: Content) -> some View { if (imageManager.image != nil) && !imageManager.isLoading { @@ -40,11 +40,17 @@ struct IndicatorViewModifier : ViewModifier where T : View { return AnyView( ZStack { content - indicator.builder($imageManager.isLoading, $imageManager.progress) + indicatorView } ) } } + + init(imageManager: ImageManager, indicator: Indicator) { + self.imageManager = imageManager + // This syntax looks not Swifty, hope for SwiftUI better design + self.indicatorView = indicator.builder(_imageManager.projectedValue.isLoading, _imageManager.projectedValue.progress) + } } #if os(macOS) || os(iOS) || os(tvOS) From 1acafa91310441cd2de2b72c30586c15e491df08 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Nov 2019 04:18:51 +0800 Subject: [PATCH 2/3] Fix Indicator logic, no longer populate when is not loading (such as error) --- SDWebImageSwiftUI/Classes/ImageManager.swift | 1 + SDWebImageSwiftUI/Classes/Indicator/Indicator.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index 755059c8..8c8ae2b7 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -34,6 +34,7 @@ class ImageManager : ObservableObject { if currentOperation != nil { return } + self.image = nil self.isLoading = true currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in guard let self = self else { diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 5db7e6ed..47ef1327 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -32,7 +32,7 @@ struct IndicatorViewModifier : ViewModifier where T : View { let indicatorView: T func body(content: Content) -> some View { - if (imageManager.image != nil) && !imageManager.isLoading { + if !imageManager.isLoading { // Disable Indiactor return AnyView(content) } else { From a00e9cbe9128449f843058074fed331949c27d94 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 2 Nov 2019 04:21:48 +0800 Subject: [PATCH 3/3] Update demo, add one SwiftUI activity indicator for watchOS --- .../project.pbxproj | 10 +++++ .../SDWebImageSwiftUIDemo/ActivityBar.swift | 37 +++++++++++++++++++ .../SDWebImageSwiftUIDemo/ContentView.swift | 5 +++ 3 files changed, 52 insertions(+) create mode 100644 Example/SDWebImageSwiftUIDemo/ActivityBar.swift diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 9cf0779e..8a11750a 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -41,6 +41,10 @@ 32E529682348A10C00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529692348A10C00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; 32E5296A2348A10C00EA46FF /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; }; + 32E7F121236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; + 32E7F122236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; + 32E7F123236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; + 32E7F124236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; 6AE7BEBA0EA1DD0FD6F99D99 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B1D3C77C4221915C839DC78 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */; }; CECA1658ECBAF54E3FF3EF58 /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A371F81C3B5BD6972F7A0E2 /* Pods_SDWebImageSwiftUIDemo.framework */; }; E8596B8000E7DC96D492333B /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 188B93ED6CBDC186E30A49C8 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */; }; @@ -130,6 +134,7 @@ 32E529512348A0DF00EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 32E7F120236CAAB8001688BC /* ActivityBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityBar.swift; sourceTree = ""; }; 33E5ED2426DFF5E06C9A2FAB /* Pods_SDWebImageSwiftUIDemo_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 48BE9486C7BDF4F74C8BA94D /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; sourceTree = ""; }; @@ -220,6 +225,7 @@ 320CDC2F22FADB44007CF858 /* ContentView.swift */, 326B0D702345C01900D28269 /* DetailView.swift */, 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */, + 32E7F120236CAAB8001688BC /* ActivityBar.swift */, 320CDC3122FADB45007CF858 /* Assets.xcassets */, 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */, 320CDC3922FADB45007CF858 /* Info.plist */, @@ -803,6 +809,7 @@ 326B0D712345C01900D28269 /* DetailView.swift in Sources */, 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */, 321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */, + 32E7F121236CAAB8001688BC /* ActivityBar.swift in Sources */, 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -814,6 +821,7 @@ 32E529622348A10B00EA46FF /* ContentView.swift in Sources */, 32E529632348A10B00EA46FF /* DetailView.swift in Sources */, 32E529642348A10B00EA46FF /* ProgressBar.swift in Sources */, + 32E7F122236CAAB8001688BC /* ActivityBar.swift in Sources */, 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -825,6 +833,7 @@ 32E529652348A10B00EA46FF /* ContentView.swift in Sources */, 32E529662348A10B00EA46FF /* DetailView.swift in Sources */, 32E529672348A10B00EA46FF /* ProgressBar.swift in Sources */, + 32E7F123236CAAB8001688BC /* ActivityBar.swift in Sources */, 32E529232348A0D300EA46FF /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -837,6 +846,7 @@ 32E529692348A10C00EA46FF /* DetailView.swift in Sources */, 32E529502348A0DE00EA46FF /* ExtensionDelegate.swift in Sources */, 32E5296A2348A10C00EA46FF /* ProgressBar.swift in Sources */, + 32E7F124236CAAB8001688BC /* ActivityBar.swift in Sources */, 32E529682348A10C00EA46FF /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SDWebImageSwiftUIDemo/ActivityBar.swift b/Example/SDWebImageSwiftUIDemo/ActivityBar.swift new file mode 100644 index 00000000..f49004dd --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo/ActivityBar.swift @@ -0,0 +1,37 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +import SwiftUI + +/// A dot circle view that depicts the active status of a task. +struct ActivityBar: View { + private var dotRadius: CGFloat = 5 + @State private var isAnimating: Bool = false + + var body: some View { + GeometryReader { (geometry: GeometryProxy) in + ForEach(0..<5) { index in + Group { + Circle() + .frame(width: self.dotRadius, height: self.dotRadius) + .scaleEffect(!self.isAnimating ? 1 - CGFloat(index) / 5 : 0.2 + CGFloat(index) / 5) + .offset(y: geometry.size.width / 10 - geometry.size.height / 2) + } + .frame(width: geometry.size.width, height: geometry.size.height) + .rotationEffect(!self.isAnimating ? .degrees(0) : .degrees(360)) + .animation(Animation + .timingCurve(0.5, 0.15 + Double(index) / 5, 0.25, 1, duration: 1.5) + .repeatForever(autoreverses: false)) + } + } + .aspectRatio(1, contentMode: .fit) + .onAppear { + self.isAnimating = true + } + } +} diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index db42cea2..f6ca5953 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -107,6 +107,11 @@ struct ContentView: View { #else WebImage(url: URL(string:url)) .resizable() + .indicator { _, _ in + ActivityBar() + .foregroundColor(Color.white) + .frame(width: 50, height: 50) + } .animation(.easeInOut(duration: 0.5)) .transition(.fade) .scaledToFit()