@@ -11,7 +11,7 @@ import SDWebImage
11
11
12
12
#if os(iOS) || os(tvOS) || os(macOS)
13
13
14
- /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit/WatchKit .
14
+ /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit.
15
15
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
16
16
public final class AnimatedImageCoordinator : NSObject {
17
17
@@ -37,6 +37,14 @@ final class AnimatedImageModel : ObservableObject {
37
37
@Published var scale : CGFloat = 1
38
38
}
39
39
40
+ /// Loading Binding Object, only properties in this object can support changes from user with @State and refresh
41
+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
42
+ final class AnimatedLoadingModel : ObservableObject , IndicatorReportable {
43
+ @Published var image : PlatformImage ? // loaded image, note when progressive loading, this will published multiple times with different partial image
44
+ @Published var isLoading : Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
45
+ @Published var progress : Double = 0 // network progress, should only be used for indicator binding
46
+ }
47
+
40
48
/// Completion Handler Binding Object, supports dynamic @State changes
41
49
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
42
50
final class AnimatedImageHandler : ObservableObject {
@@ -81,6 +89,7 @@ final class AnimatedImageConfiguration: ObservableObject {
81
89
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
82
90
public struct AnimatedImage : PlatformViewRepresentable {
83
91
@ObservedObject var imageModel = AnimatedImageModel ( )
92
+ @ObservedObject var imageLoading = AnimatedLoadingModel ( )
84
93
@ObservedObject var imageHandler = AnimatedImageHandler ( )
85
94
@ObservedObject var imageLayout = AnimatedImageLayout ( )
86
95
@ObservedObject var imageConfiguration = AnimatedImageConfiguration ( )
@@ -193,18 +202,31 @@ public struct AnimatedImage : PlatformViewRepresentable {
193
202
if currentOperation != nil {
194
203
return
195
204
}
205
+ self . imageLoading. isLoading = true
196
206
view. wrapped. sd_setImage ( with: imageModel. url, placeholderImage: imageConfiguration. placeholder, options: imageModel. webOptions, context: imageModel. webContext, progress: { ( receivedSize, expectedSize, _) in
207
+ let progress : Double
208
+ if ( expectedSize > 0 ) {
209
+ progress = Double ( receivedSize) / Double( expectedSize)
210
+ } else {
211
+ progress = 0
212
+ }
213
+ DispatchQueue . main. async {
214
+ self . imageLoading. progress = progress
215
+ }
197
216
self . imageHandler. progressBlock ? ( receivedSize, expectedSize)
198
217
} ) { ( image, error, cacheType, _) in
199
218
// This is a hack because of Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call
200
- // Here I have to use UIKit API to triger the same effect (the window change implicitly cause re-render)
219
+ // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render)
201
220
if let hostingView = AnimatedImage . findHostingView ( from: view) {
202
221
#if os(macOS)
203
222
hostingView. viewDidMoveToWindow ( )
204
223
#else
205
224
hostingView. didMoveToWindow ( )
206
225
#endif
207
226
}
227
+ self . imageLoading. image = image
228
+ self . imageLoading. isLoading = false
229
+ self . imageLoading. progress = 1
208
230
if let image = image {
209
231
self . imageHandler. successBlock ? ( image, cacheType)
210
232
} else {
@@ -704,7 +726,7 @@ extension AnimatedImage {
704
726
}
705
727
}
706
728
707
- // Web Image convenience
729
+ // Web Image convenience, based on UIKit/AppKit API
708
730
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
709
731
extension AnimatedImage {
710
732
@@ -732,6 +754,23 @@ extension AnimatedImage {
732
754
}
733
755
}
734
756
757
+ // Indicator
758
+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
759
+ extension AnimatedImage {
760
+
761
+ /// Associate a indicator when loading image with url
762
+ /// - Parameter indicator: The indicator type, see `Indicator`
763
+ public func indicator< T> ( _ indicator: Indicator < T > ) -> some View where T : View {
764
+ return self . modifier ( IndicatorViewModifier ( reporter: self . imageLoading, indicator: indicator) )
765
+ }
766
+
767
+ /// Associate a indicator when loading image with url, convenient method with block
768
+ /// - Parameter content: A view that describes the indicator.
769
+ public func indicator< T> ( @ViewBuilder content: @escaping ( _ isAnimating: Binding < Bool > , _ progress: Binding < Double > ) -> T ) -> some View where T : View {
770
+ return indicator ( Indicator ( content: content) )
771
+ }
772
+ }
773
+
735
774
#if DEBUG
736
775
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
737
776
struct AnimatedImage_Previews : PreviewProvider {
0 commit comments