@@ -43,6 +43,11 @@ final class AnimatedLoadingModel : ObservableObject, IndicatorReportable {
4343 @Published var image : PlatformImage ? // loaded image, note when progressive loading, this will published multiple times with different partial image
4444 @Published var isLoading : Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
4545 @Published var progress : Double = 0 // network progress, should only be used for indicator binding
46+
47+ /// Used for loading status recording to avoid recursive `updateView`. There are 3 types of loading (Name/Data/URL)
48+ @Published var imageName : String ?
49+ @Published var imageData : Data ?
50+ @Published var imageURL : URL ?
4651}
4752
4853/// Completion Handler Binding Object, supports dynamic @State changes
@@ -201,12 +206,27 @@ public struct AnimatedImage : PlatformViewRepresentable {
201206 }
202207 #endif
203208
204- func loadImage( _ view: AnimatedImageViewWrapper , context: Context ) {
205- let operationKey = NSStringFromClass ( type ( of: view. wrapped) )
206- let currentOperation = view. wrapped. sd_imageLoadOperation ( forKey: operationKey)
207- if currentOperation != nil {
208- return
209+ func setupIndicator( _ view: AnimatedImageViewWrapper , context: Context ) {
210+ view. wrapped. sd_imageIndicator = imageConfiguration. indicator
211+ view. wrapped. sd_imageTransition = imageConfiguration. transition
212+ if let placeholderView = imageConfiguration. placeholderView {
213+ placeholderView. removeFromSuperview ( )
214+ placeholderView. isHidden = true
215+ // Placeholder View should below the Indicator View
216+ if let indicatorView = imageConfiguration. indicator? . indicatorView {
217+ #if os(macOS)
218+ view. wrapped. addSubview ( placeholderView, positioned: . below, relativeTo: indicatorView)
219+ #else
220+ view. wrapped. insertSubview ( placeholderView, belowSubview: indicatorView)
221+ #endif
222+ } else {
223+ view. wrapped. addSubview ( placeholderView)
224+ }
225+ placeholderView. bindFrameToSuperviewBounds ( )
209226 }
227+ }
228+
229+ func loadImage( _ view: AnimatedImageViewWrapper , context: Context ) {
210230 self . imageLoading. isLoading = true
211231 let options = imageModel. webOptions
212232 if options. contains ( . delayPlaceholder) {
@@ -228,15 +248,19 @@ public struct AnimatedImage : PlatformViewRepresentable {
228248 }
229249 self . imageHandler. progressBlock ? ( receivedSize, expectedSize)
230250 } ) { ( image, data, error, cacheType, finished, _) in
231- // This is a hack because of Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call
232- // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render)
233- if let hostingView = AnimatedImage . findHostingView ( from: view) {
234- if let _ = hostingView. window {
235- #if os(macOS)
236- hostingView. viewDidMoveToWindow ( )
237- #else
238- hostingView. didMoveToWindow ( )
239- #endif
251+ if #available( iOS 14 . 0 , macOS 11 . 0 , watchOS 7 . 0 , tvOS 14 . 0 , * ) {
252+ // Do nothing. on iOS 14's SwiftUI, the @Published will always trigger another `updateUIView` call with new UIView instance.
253+ } else {
254+ // This is a hack because of iOS 13's SwiftUI bug, the @Published does not trigger another `updateUIView` call
255+ // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render)
256+ if let hostingView = AnimatedImage . findHostingView ( from: view) {
257+ if let _ = hostingView. window {
258+ #if os(macOS)
259+ hostingView. viewDidMoveToWindow ( )
260+ #else
261+ hostingView. didMoveToWindow ( )
262+ #endif
263+ }
240264 }
241265 }
242266 self . imageLoading. image = image
@@ -263,37 +287,40 @@ public struct AnimatedImage : PlatformViewRepresentable {
263287 func updateView( _ view: AnimatedImageViewWrapper , context: Context ) {
264288 // Refresh image, imageModel is the Source of Truth, switch the type
265289 // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost.
266- if let name = imageModel. name, name != view . wrapped . sd_imageName {
290+ if let name = imageModel. name, name != imageLoading . imageName {
267291 #if os(macOS)
268292 let image = SDAnimatedImage ( named: name, in: imageModel. bundle)
269293 #else
270294 let image = SDAnimatedImage ( named: name, in: imageModel. bundle, compatibleWith: nil )
271295 #endif
272- view . wrapped . sd_imageName = name
296+ imageLoading . imageName = name
273297 view. wrapped. image = image
274- } else if let data = imageModel. data, data != view . wrapped . sd_imageData {
298+ } else if let data = imageModel. data, data != imageLoading . imageData {
275299 let image = SDAnimatedImage ( data: data, scale: imageModel. scale)
276- view . wrapped . sd_imageData = data
300+ imageLoading . imageData = data
277301 view. wrapped. image = image
278- } else if let url = imageModel. url, url != view. wrapped. sd_imageURL {
279- view. wrapped. sd_imageIndicator = imageConfiguration. indicator
280- view. wrapped. sd_imageTransition = imageConfiguration. transition
281- if let placeholderView = imageConfiguration. placeholderView {
282- placeholderView. removeFromSuperview ( )
283- placeholderView. isHidden = true
284- // Placeholder View should below the Indicator View
285- if let indicatorView = imageConfiguration. indicator? . indicatorView {
286- #if os(macOS)
287- view. wrapped. addSubview ( placeholderView, positioned: . below, relativeTo: indicatorView)
288- #else
289- view. wrapped. insertSubview ( placeholderView, belowSubview: indicatorView)
290- #endif
302+ } else if let url = imageModel. url {
303+ // Determine if image already been loaded and URL is match
304+ var shouldLoad : Bool
305+ if url != imageLoading. imageURL {
306+ // Change the URL, need new loading
307+ shouldLoad = true
308+ imageLoading. imageURL = url
309+ } else {
310+ // Same URL, check if already loaded
311+ if imageLoading. isLoading {
312+ shouldLoad = false
313+ } else if let image = imageLoading. image {
314+ shouldLoad = false
315+ view. wrapped. image = image
291316 } else {
292- view . wrapped . addSubview ( placeholderView )
317+ shouldLoad = true
293318 }
294- placeholderView. bindFrameToSuperviewBounds ( )
295319 }
296- loadImage ( view, context: context)
320+ if shouldLoad {
321+ setupIndicator ( view, context: context)
322+ loadImage ( view, context: context)
323+ }
297324 }
298325
299326 #if os(macOS)
0 commit comments