@@ -20,6 +20,8 @@ public final class AnimatedImageCoordinator: NSObject {
20
20
21
21
/// Any user-provided info stored into coordinator, such as status value used for coordinator
22
22
public var userInfo : [ AnyHashable : Any ] ?
23
+
24
+ var imageLoading = AnimatedLoadingModel ( )
23
25
}
24
26
25
27
/// Data Binding Object, only properties in this object can support changes from user with @State and refresh
@@ -41,6 +43,8 @@ final class AnimatedImageModel : ObservableObject {
41
43
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
42
44
final class AnimatedLoadingModel : ObservableObject {
43
45
@Published var image : PlatformImage ? // loaded image, note when progressive loading, this will published multiple times with different partial image
46
+ @Published var isLoading : Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
47
+ @Published var progress : Double = 0 // network progress, should only be used for indicator binding
44
48
45
49
/// Used for loading status recording to avoid recursive `updateView`. There are 3 types of loading (Name/Data/URL)
46
50
@Published var imageName : String ?
@@ -97,11 +101,18 @@ final class AnimatedImageConfiguration: ObservableObject {
97
101
/// A Image View type to load image from url, data or bundle. Supports animated and static image format.
98
102
@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
99
103
public struct AnimatedImage : PlatformViewRepresentable {
100
- @Backport . StateObject var imageModel = AnimatedImageModel ( )
101
- @Backport . StateObject var imageLoading = AnimatedLoadingModel ( )
102
- @Backport . StateObject var imageHandler = AnimatedImageHandler ( )
103
- @Backport . StateObject var imageLayout = AnimatedImageLayout ( )
104
- @Backport . StateObject var imageConfiguration = AnimatedImageConfiguration ( )
104
+ @SwiftUI . StateObject var imageModel_SwiftUI = AnimatedImageModel ( )
105
+ @Backport . StateObject var imageModel_Backport = AnimatedImageModel ( )
106
+ var imageModel : AnimatedImageModel {
107
+ if #available( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
108
+ return imageModel_SwiftUI
109
+ } else {
110
+ return imageModel_Backport
111
+ }
112
+ }
113
+ @ObservedObject var imageHandler = AnimatedImageHandler ( )
114
+ @ObservedObject var imageLayout = AnimatedImageLayout ( )
115
+ @ObservedObject var imageConfiguration = AnimatedImageConfiguration ( )
105
116
106
117
/// A observed object to pass through the image manager loading status to indicator
107
118
@ObservedObject var indicatorStatus = IndicatorStatus ( )
@@ -128,10 +139,11 @@ public struct AnimatedImage : PlatformViewRepresentable {
128
139
/// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
129
140
/// - Parameter isAnimating: The binding for animation control
130
141
public init ( url: URL ? , options: SDWebImageOptions = [ ] , context: [ SDWebImageContextOption : Any ] ? = nil , isAnimating: Binding < Bool > ) {
131
- self . _isAnimating = isAnimating
132
- self . imageModel. url = url
133
- self . imageModel. webOptions = options
134
- self . imageModel. webContext = context
142
+ let imageModel = AnimatedImageModel ( )
143
+ imageModel. url = url
144
+ imageModel. webOptions = options
145
+ imageModel. webContext = context
146
+ self . init ( imageModel: imageModel, isAnimating: isAnimating)
135
147
}
136
148
137
149
/// Create an animated image with name and bundle.
@@ -148,9 +160,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
148
160
/// - Parameter bundle: The bundle contains image
149
161
/// - Parameter isAnimating: The binding for animation control
150
162
public init ( name: String , bundle: Bundle ? = nil , isAnimating: Binding < Bool > ) {
151
- self . _isAnimating = isAnimating
152
- self . imageModel. name = name
153
- self . imageModel. bundle = bundle
163
+ let imageModel = AnimatedImageModel ( )
164
+ imageModel. name = name
165
+ imageModel. bundle = bundle
166
+ self . init ( imageModel: imageModel, isAnimating: isAnimating)
154
167
}
155
168
156
169
/// Create an animated image with data and scale.
@@ -165,9 +178,19 @@ public struct AnimatedImage : PlatformViewRepresentable {
165
178
/// - Parameter scale: The scale factor
166
179
/// - Parameter isAnimating: The binding for animation control
167
180
public init ( data: Data , scale: CGFloat = 1 , isAnimating: Binding < Bool > ) {
181
+ let imageModel = AnimatedImageModel ( )
182
+ imageModel. data = data
183
+ imageModel. scale = scale
184
+ self . init ( imageModel: imageModel, isAnimating: isAnimating)
185
+ }
186
+
187
+ init ( imageModel: AnimatedImageModel , isAnimating: Binding < Bool > ) {
168
188
self . _isAnimating = isAnimating
169
- self . imageModel. data = data
170
- self . imageModel. scale = scale
189
+ if #available( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
190
+ _imageModel_SwiftUI = SwiftUI . StateObject ( wrappedValue: imageModel)
191
+ } else {
192
+ _imageModel_Backport = Backport . StateObject ( wrappedValue: imageModel)
193
+ }
171
194
}
172
195
173
196
#if os(macOS)
@@ -183,23 +206,23 @@ public struct AnimatedImage : PlatformViewRepresentable {
183
206
}
184
207
185
208
#if os(macOS)
186
- public func makeNSView( context: NSViewRepresentableContext < AnimatedImage > ) -> AnimatedImageViewWrapper {
209
+ public func makeNSView( context: Context ) -> AnimatedImageViewWrapper {
187
210
makeView ( context: context)
188
211
}
189
212
190
- public func updateNSView( _ nsView: AnimatedImageViewWrapper , context: NSViewRepresentableContext < AnimatedImage > ) {
213
+ public func updateNSView( _ nsView: AnimatedImageViewWrapper , context: Context ) {
191
214
updateView ( nsView, context: context)
192
215
}
193
216
194
217
public static func dismantleNSView( _ nsView: AnimatedImageViewWrapper , coordinator: Coordinator ) {
195
218
dismantleView ( nsView, coordinator: coordinator)
196
219
}
197
220
#elseif os(iOS) || os(tvOS)
198
- public func makeUIView( context: UIViewRepresentableContext < AnimatedImage > ) -> AnimatedImageViewWrapper {
221
+ public func makeUIView( context: Context ) -> AnimatedImageViewWrapper {
199
222
makeView ( context: context)
200
223
}
201
224
202
- public func updateUIView( _ uiView: AnimatedImageViewWrapper , context: UIViewRepresentableContext < AnimatedImage > ) {
225
+ public func updateUIView( _ uiView: AnimatedImageViewWrapper , context: Context ) {
203
226
updateView ( uiView, context: context)
204
227
}
205
228
@@ -229,24 +252,24 @@ public struct AnimatedImage : PlatformViewRepresentable {
229
252
}
230
253
231
254
func loadImage( _ view: AnimatedImageViewWrapper , context: Context ) {
232
- self . indicatorStatus . isLoading = true
233
- let options = imageModel. webOptions
234
- if options . contains ( . delayPlaceholder) {
255
+ context . coordinator . imageLoading . isLoading = true
256
+ let webOptions = imageModel. webOptions
257
+ if webOptions . contains ( . delayPlaceholder) {
235
258
self . imageConfiguration. placeholderView? . isHidden = true
236
259
} else {
237
260
self . imageConfiguration. placeholderView? . isHidden = false
238
261
}
239
- var context = imageModel. webContext ?? [ : ]
240
- context [ . animatedImageClass] = SDAnimatedImage . self
241
- view. wrapped. sd_internalSetImage ( with: imageModel. url, placeholderImage: imageConfiguration. placeholder, options: options , context: context , setImageBlock: nil , progress: { ( receivedSize, expectedSize, _) in
262
+ var webContext = imageModel. webContext ?? [ : ]
263
+ webContext [ . animatedImageClass] = SDAnimatedImage . self
264
+ view. wrapped. sd_internalSetImage ( with: imageModel. url, placeholderImage: imageConfiguration. placeholder, options: webOptions , context: webContext , setImageBlock: nil , progress: { ( receivedSize, expectedSize, _) in
242
265
let progress : Double
243
266
if ( expectedSize > 0 ) {
244
267
progress = Double ( receivedSize) / Double( expectedSize)
245
268
} else {
246
269
progress = 0
247
270
}
248
271
DispatchQueue . main. async {
249
- self . indicatorStatus . progress = progress
272
+ context . coordinator . imageLoading . progress = progress
250
273
}
251
274
self . imageHandler. progressBlock ? ( receivedSize, expectedSize)
252
275
} ) { ( image, data, error, cacheType, finished, _) in
@@ -265,9 +288,9 @@ public struct AnimatedImage : PlatformViewRepresentable {
265
288
}
266
289
}
267
290
}
268
- self . imageLoading. image = image
269
- self . indicatorStatus . isLoading = false
270
- self . indicatorStatus . progress = 1
291
+ context . coordinator . imageLoading. image = image
292
+ context . coordinator . imageLoading . isLoading = false
293
+ context . coordinator . imageLoading . progress = 1
271
294
if let image = image {
272
295
self . imageConfiguration. placeholderView? . isHidden = true
273
296
self . imageHandler. successBlock ? ( image, data, cacheType)
@@ -289,30 +312,30 @@ public struct AnimatedImage : PlatformViewRepresentable {
289
312
func updateView( _ view: AnimatedImageViewWrapper , context: Context ) {
290
313
// Refresh image, imageModel is the Source of Truth, switch the type
291
314
// Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost.
292
- if let name = imageModel. name, name != imageLoading. imageName {
315
+ if let name = imageModel. name, name != context . coordinator . imageLoading. imageName {
293
316
#if os(macOS)
294
317
let image = SDAnimatedImage ( named: name, in: imageModel. bundle)
295
318
#else
296
319
let image = SDAnimatedImage ( named: name, in: imageModel. bundle, compatibleWith: nil )
297
320
#endif
298
- imageLoading. imageName = name
321
+ context . coordinator . imageLoading. imageName = name
299
322
view. wrapped. image = image
300
- } else if let data = imageModel. data, data != imageLoading. imageData {
323
+ } else if let data = imageModel. data, data != context . coordinator . imageLoading. imageData {
301
324
let image = SDAnimatedImage ( data: data, scale: imageModel. scale)
302
- imageLoading. imageData = data
325
+ context . coordinator . imageLoading. imageData = data
303
326
view. wrapped. image = image
304
327
} else if let url = imageModel. url {
305
328
// Determine if image already been loaded and URL is match
306
329
var shouldLoad : Bool
307
- if url != imageLoading. imageURL {
330
+ if url != context . coordinator . imageLoading. imageURL {
308
331
// Change the URL, need new loading
309
332
shouldLoad = true
310
- imageLoading. imageURL = url
333
+ context . coordinator . imageLoading. imageURL = url
311
334
} else {
312
335
// Same URL, check if already loaded
313
- if indicatorStatus . isLoading {
336
+ if context . coordinator . imageLoading . isLoading {
314
337
shouldLoad = false
315
- } else if let image = imageLoading. image {
338
+ } else if let image = context . coordinator . imageLoading. image {
316
339
shouldLoad = false
317
340
view. wrapped. image = image
318
341
} else {
0 commit comments