@@ -20,6 +20,8 @@ public final class AnimatedImageCoordinator: NSObject {
2020
2121 /// Any user-provided info stored into coordinator, such as status value used for coordinator
2222 public var userInfo : [ AnyHashable : Any ] ?
23+
24+ var imageLoading = AnimatedLoadingModel ( )
2325}
2426
2527/// 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 {
4143@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
4244final class AnimatedLoadingModel : ObservableObject {
4345 @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
4448
4549 /// Used for loading status recording to avoid recursive `updateView`. There are 3 types of loading (Name/Data/URL)
4650 @Published var imageName : String ?
@@ -97,11 +101,18 @@ final class AnimatedImageConfiguration: ObservableObject {
97101/// A Image View type to load image from url, data or bundle. Supports animated and static image format.
98102@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
99103public 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 ( )
105116
106117 /// A observed object to pass through the image manager loading status to indicator
107118 @ObservedObject var indicatorStatus = IndicatorStatus ( )
@@ -128,10 +139,11 @@ public struct AnimatedImage : PlatformViewRepresentable {
128139 /// - 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.
129140 /// - Parameter isAnimating: The binding for animation control
130141 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)
135147 }
136148
137149 /// Create an animated image with name and bundle.
@@ -148,9 +160,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
148160 /// - Parameter bundle: The bundle contains image
149161 /// - Parameter isAnimating: The binding for animation control
150162 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)
154167 }
155168
156169 /// Create an animated image with data and scale.
@@ -165,9 +178,19 @@ public struct AnimatedImage : PlatformViewRepresentable {
165178 /// - Parameter scale: The scale factor
166179 /// - Parameter isAnimating: The binding for animation control
167180 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 > ) {
168188 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+ }
171194 }
172195
173196 #if os(macOS)
@@ -183,23 +206,23 @@ public struct AnimatedImage : PlatformViewRepresentable {
183206 }
184207
185208 #if os(macOS)
186- public func makeNSView( context: NSViewRepresentableContext < AnimatedImage > ) -> AnimatedImageViewWrapper {
209+ public func makeNSView( context: Context ) -> AnimatedImageViewWrapper {
187210 makeView ( context: context)
188211 }
189212
190- public func updateNSView( _ nsView: AnimatedImageViewWrapper , context: NSViewRepresentableContext < AnimatedImage > ) {
213+ public func updateNSView( _ nsView: AnimatedImageViewWrapper , context: Context ) {
191214 updateView ( nsView, context: context)
192215 }
193216
194217 public static func dismantleNSView( _ nsView: AnimatedImageViewWrapper , coordinator: Coordinator ) {
195218 dismantleView ( nsView, coordinator: coordinator)
196219 }
197220 #elseif os(iOS) || os(tvOS)
198- public func makeUIView( context: UIViewRepresentableContext < AnimatedImage > ) -> AnimatedImageViewWrapper {
221+ public func makeUIView( context: Context ) -> AnimatedImageViewWrapper {
199222 makeView ( context: context)
200223 }
201224
202- public func updateUIView( _ uiView: AnimatedImageViewWrapper , context: UIViewRepresentableContext < AnimatedImage > ) {
225+ public func updateUIView( _ uiView: AnimatedImageViewWrapper , context: Context ) {
203226 updateView ( uiView, context: context)
204227 }
205228
@@ -229,24 +252,24 @@ public struct AnimatedImage : PlatformViewRepresentable {
229252 }
230253
231254 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) {
235258 self . imageConfiguration. placeholderView? . isHidden = true
236259 } else {
237260 self . imageConfiguration. placeholderView? . isHidden = false
238261 }
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
242265 let progress : Double
243266 if ( expectedSize > 0 ) {
244267 progress = Double ( receivedSize) / Double( expectedSize)
245268 } else {
246269 progress = 0
247270 }
248271 DispatchQueue . main. async {
249- self . indicatorStatus . progress = progress
272+ context . coordinator . imageLoading . progress = progress
250273 }
251274 self . imageHandler. progressBlock ? ( receivedSize, expectedSize)
252275 } ) { ( image, data, error, cacheType, finished, _) in
@@ -265,9 +288,9 @@ public struct AnimatedImage : PlatformViewRepresentable {
265288 }
266289 }
267290 }
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
271294 if let image = image {
272295 self . imageConfiguration. placeholderView? . isHidden = true
273296 self . imageHandler. successBlock ? ( image, data, cacheType)
@@ -289,30 +312,30 @@ public struct AnimatedImage : PlatformViewRepresentable {
289312 func updateView( _ view: AnimatedImageViewWrapper , context: Context ) {
290313 // Refresh image, imageModel is the Source of Truth, switch the type
291314 // 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 {
293316 #if os(macOS)
294317 let image = SDAnimatedImage ( named: name, in: imageModel. bundle)
295318 #else
296319 let image = SDAnimatedImage ( named: name, in: imageModel. bundle, compatibleWith: nil )
297320 #endif
298- imageLoading. imageName = name
321+ context . coordinator . imageLoading. imageName = name
299322 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 {
301324 let image = SDAnimatedImage ( data: data, scale: imageModel. scale)
302- imageLoading. imageData = data
325+ context . coordinator . imageLoading. imageData = data
303326 view. wrapped. image = image
304327 } else if let url = imageModel. url {
305328 // Determine if image already been loaded and URL is match
306329 var shouldLoad : Bool
307- if url != imageLoading. imageURL {
330+ if url != context . coordinator . imageLoading. imageURL {
308331 // Change the URL, need new loading
309332 shouldLoad = true
310- imageLoading. imageURL = url
333+ context . coordinator . imageLoading. imageURL = url
311334 } else {
312335 // Same URL, check if already loaded
313- if indicatorStatus . isLoading {
336+ if context . coordinator . imageLoading . isLoading {
314337 shouldLoad = false
315- } else if let image = imageLoading. image {
338+ } else if let image = context . coordinator . imageLoading. image {
316339 shouldLoad = false
317340 view. wrapped. image = image
318341 } else {
0 commit comments