Skip to content

Commit ef03282

Browse files
committed
Fix the WebImage onSuccess does not get called because of StateObject get touched before onAppear
1 parent ac0e73b commit ef03282

File tree

1 file changed

+57
-28
lines changed

1 file changed

+57
-28
lines changed

SDWebImageSwiftUI/Classes/WebImage.swift

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,46 @@
99
import SwiftUI
1010
import SDWebImage
1111

12+
/// Completion Handler Binding Object, supports dynamic @State changes
13+
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
14+
final class WebImageHandler: ObservableObject {
15+
// Completion Handler
16+
@Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
17+
@Published var failureBlock: ((Error) -> Void)?
18+
@Published var progressBlock: ((Int, Int) -> Void)?
19+
}
20+
21+
/// Configuration Binding Object, supports dynamic @State changes
22+
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
23+
final class WebImageConfiguration: ObservableObject {
24+
var retryOnAppear: Bool = true
25+
var cancelOnDisappear: Bool = true
26+
var maxBufferSize: UInt?
27+
var customLoopCount: UInt?
28+
var runLoopMode: RunLoop.Mode = .common
29+
var pausable: Bool = true
30+
var purgeable: Bool = false
31+
var playbackRate: Double = 1.0
32+
var playbackMode: SDAnimatedImagePlaybackMode = .normal
33+
}
34+
1235
/// A Image View type to load image from url. Supports static/animated image format.
1336
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
1437
public struct WebImage : View {
1538
var configurations: [(Image) -> Image] = []
1639

1740
var placeholder: AnyView?
18-
var retryOnAppear: Bool = true
19-
var cancelOnDisappear: Bool = true
20-
var pausable: Bool = true
21-
var purgeable: Bool = false
2241

2342
/// A Binding to control the animation. You can bind external logic to control the animation status.
2443
/// True to start animation, false to stop animation.
2544
@Binding public var isAnimating: Bool
2645

46+
/// A observed object to pass through the image handler to manager
47+
@ObservedObject var imageHandler = WebImageHandler()
48+
49+
/// A observed object to pass through the image configuration to player
50+
@ObservedObject var imageConfiguration = WebImageConfiguration()
51+
2752
/// A observed object to pass through the image manager loading status to indicator
2853
@ObservedObject var indicatorStatus = IndicatorStatus()
2954

@@ -84,12 +109,12 @@ public struct WebImage : View {
84109
.onDisappear {
85110
// Only stop the player which is not intermediate status
86111
if !imagePlayer.isWaiting {
87-
if self.pausable {
112+
if self.imageConfiguration.pausable {
88113
self.imagePlayer.pausePlaying()
89114
} else {
90115
self.imagePlayer.stopPlaying()
91116
}
92-
if self.purgeable {
117+
if self.imageConfiguration.purgeable {
93118
self.imagePlayer.clearFrameBuffer()
94119
}
95120
}
@@ -104,15 +129,18 @@ public struct WebImage : View {
104129
} else {
105130
setupPlaceholder()
106131
.onAppear {
132+
self.imageManager.successBlock = self.imageHandler.successBlock
133+
self.imageManager.failureBlock = self.imageHandler.failureBlock
134+
self.imageManager.progressBlock = self.imageHandler.progressBlock
107135
// Load remote image when first appear
108136
self.imageManager.load()
109-
guard self.retryOnAppear else { return }
137+
guard self.imageConfiguration.retryOnAppear else { return }
110138
// When using prorgessive loading, the new partial image will cause onAppear. Filter this case
111139
if self.imageManager.image == nil && !self.imageManager.isIncremental {
112140
self.imageManager.load()
113141
}
114142
}.onDisappear {
115-
guard self.cancelOnDisappear else { return }
143+
guard self.imageConfiguration.cancelOnDisappear else { return }
116144
// When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case
117145
if self.imageManager.image == nil && !self.imageManager.isIncremental {
118146
self.imageManager.cancel()
@@ -183,6 +211,11 @@ public struct WebImage : View {
183211
} else {
184212
return configure(image: imageManager.image!).onAppear {
185213
if let animatedImage = imageManager.image as? SDAnimatedImageProvider {
214+
self.imagePlayer.customLoopCount = self.imageConfiguration.customLoopCount
215+
self.imagePlayer.maxBufferSize = self.imageConfiguration.maxBufferSize
216+
self.imagePlayer.runLoopMode = self.imageConfiguration.runLoopMode
217+
self.imagePlayer.playbackMode = self.imageConfiguration.playbackMode
218+
self.imagePlayer.playbackRate = self.imageConfiguration.playbackRate
186219
self.imagePlayer.setupPlayer(animatedImage: animatedImage)
187220
self.imagePlayer.startPlaying()
188221
}
@@ -253,7 +286,7 @@ extension WebImage {
253286
/// - action: The action to perform. The first arg is the error during loading. If `action` is `nil`, the call has no effect.
254287
/// - Returns: A view that triggers `action` when this image load fails.
255288
public func onFailure(perform action: ((Error) -> Void)? = nil) -> WebImage {
256-
self.imageManager.failureBlock = action
289+
self.imageHandler.failureBlock = action
257290
return self
258291
}
259292

@@ -262,7 +295,7 @@ extension WebImage {
262295
/// - action: The action to perform. The first arg is the loaded image, the second arg is the loaded image data, the third arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
263296
/// - Returns: A view that triggers `action` when this image load successes.
264297
public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> WebImage {
265-
self.imageManager.successBlock = action
298+
self.imageHandler.successBlock = action
266299
return self
267300
}
268301

@@ -271,7 +304,7 @@ extension WebImage {
271304
/// - action: The action to perform. The first arg is the received size, the second arg is the total size, all in bytes. If `action` is `nil`, the call has no effect.
272305
/// - Returns: A view that triggers `action` when this image load successes.
273306
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> WebImage {
274-
self.imageManager.progressBlock = action
307+
self.imageHandler.progressBlock = action
275308
return self
276309
}
277310
}
@@ -303,17 +336,15 @@ extension WebImage {
303336
/// Control the behavior to retry the failed loading when view become appears again
304337
/// - Parameter flag: Whether or not to retry the failed loading
305338
public func retryOnAppear(_ flag: Bool) -> WebImage {
306-
var result = self
307-
result.retryOnAppear = flag
308-
return result
339+
self.imageConfiguration.retryOnAppear = flag
340+
return self
309341
}
310342

311343
/// Control the behavior to cancel the pending loading when view become disappear again
312344
/// - Parameter flag: Whether or not to cancel the pending loading
313345
public func cancelOnDisappear(_ flag: Bool) -> WebImage {
314-
var result = self
315-
result.cancelOnDisappear = flag
316-
return result
346+
self.imageConfiguration.cancelOnDisappear = flag
347+
return self
317348
}
318349
}
319350

@@ -342,7 +373,7 @@ extension WebImage {
342373
/// - Note: Pass nil to disable customization, use the image itself loop count (`animatedImageLoopCount`) instead
343374
/// - Parameter loopCount: The animation loop count
344375
public func customLoopCount(_ loopCount: UInt?) -> WebImage {
345-
self.imagePlayer.customLoopCount = loopCount
376+
self.imageConfiguration.customLoopCount = loopCount
346377
return self
347378
}
348379

@@ -353,7 +384,7 @@ extension WebImage {
353384
/// `UInt.max` means cache all the buffer. (Lowest CPU and Highest Memory)
354385
/// - Parameter bufferSize: The max buffer size
355386
public func maxBufferSize(_ bufferSize: UInt?) -> WebImage {
356-
self.imagePlayer.maxBufferSize = bufferSize
387+
self.imageConfiguration.maxBufferSize = bufferSize
357388
return self
358389
}
359390

@@ -362,26 +393,24 @@ extension WebImage {
362393
/// - Note: This is useful for some cases, for example, always specify NSDefaultRunLoopMode, if you want to pause the animation when user scroll (for Mac user, drag the mouse or touchpad)
363394
/// - Parameter runLoopMode: The runLoopMode for animation
364395
public func runLoopMode(_ runLoopMode: RunLoop.Mode) -> WebImage {
365-
self.imagePlayer.runLoopMode = runLoopMode
396+
self.imageConfiguration.runLoopMode = runLoopMode
366397
return self
367398
}
368399

369400
/// Whether or not to pause the animation (keep current frame), instead of stop the animation (frame index reset to 0). When `isAnimating` binding value changed to false. Defaults is true.
370401
/// - Note: For some of use case, you may want to reset the frame index to 0 when stop, but some other want to keep the current frame index.
371402
/// - Parameter pausable: Whether or not to pause the animation instead of stop the animation.
372403
public func pausable(_ pausable: Bool) -> WebImage {
373-
var result = self
374-
result.pausable = pausable
375-
return result
404+
self.imageConfiguration.pausable = pausable
405+
return self
376406
}
377407

378408
/// Whether or not to clear frame buffer cache when stopped. Defaults is false.
379409
/// Note: This is useful when you want to limit the memory usage during frequently visibility changes (such as image view inside a list view, then push and pop)
380410
/// - Parameter purgeable: Whether or not to clear frame buffer cache when stopped.
381411
public func purgeable(_ purgeable: Bool) -> WebImage {
382-
var result = self
383-
result.purgeable = purgeable
384-
return result
412+
self.imageConfiguration.purgeable = purgeable
413+
return self
385414
}
386415

387416
/// Control the animation playback rate. Default is 1.0.
@@ -392,14 +421,14 @@ extension WebImage {
392421
/// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future)
393422
/// - Parameter playbackRate: The animation playback rate.
394423
public func playbackRate(_ playbackRate: Double) -> WebImage {
395-
self.imagePlayer.playbackRate = playbackRate
424+
self.imageConfiguration.playbackRate = playbackRate
396425
return self
397426
}
398427

399428
/// Control the animation playback mode. Default is .normal
400429
/// - Parameter playbackMode: The playback mode, including normal order, reverse order, bounce order and reversed bounce order.
401430
public func playbackMode(_ playbackMode: SDAnimatedImagePlaybackMode) -> WebImage {
402-
self.imagePlayer.playbackMode = playbackMode
431+
self.imageConfiguration.playbackMode = playbackMode
403432
return self
404433
}
405434
}

0 commit comments

Comments
 (0)