99import SwiftUI
1010import SDWebImage
1111
12+ /// Data Binding Object, only properties in this object can support changes from user with @State and refresh
13+ @available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
14+ final class WebImageModel : ObservableObject {
15+ /// URL image
16+ @Published var url : URL ?
17+ @Published var webOptions : SDWebImageOptions = [ ]
18+ @Published var webContext : [ SDWebImageContextOption : Any ] ? = nil
19+ }
20+
1221/// Completion Handler Binding Object, supports dynamic @State changes
1322@available ( iOS 13 . 0 , OSX 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , * )
1423final class WebImageHandler : ObservableObject {
@@ -43,6 +52,9 @@ public struct WebImage : View {
4352 /// True to start animation, false to stop animation.
4453 @Binding public var isAnimating : Bool
4554
55+ /// A observed object to pass through the image model to manager
56+ @ObservedObject var imageModel : WebImageModel
57+
4658 /// A observed object to pass through the image handler to manager
4759 @ObservedObject var imageHandler = WebImageHandler ( )
4860
@@ -52,25 +64,10 @@ public struct WebImage : View {
5264 /// A observed object to pass through the image manager loading status to indicator
5365 @ObservedObject var indicatorStatus = IndicatorStatus ( )
5466
55- @SwiftUI . StateObject var imagePlayer_SwiftUI = ImagePlayer ( )
56- @Backport . StateObject var imagePlayer_Backport = ImagePlayer ( )
57- var imagePlayer : ImagePlayer {
58- if #available( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
59- return imagePlayer_SwiftUI
60- } else {
61- return imagePlayer_Backport
62- }
63- }
67+ @ObservedObject var imagePlayer = ImagePlayer ( )
6468
65- @SwiftUI . StateObject var imageManager_SwiftUI = ImageManager ( )
66- @Backport . StateObject var imageManager_Backport = ImageManager ( )
67- var imageManager : ImageManager {
68- if #available( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
69- return imageManager_SwiftUI
70- } else {
71- return imageManager_Backport
72- }
73- }
69+ // FIXME: Use SwiftUI StateObject and remove onPlatformAppear once drop iOS 13 support
70+ @Backport . StateObject var imageManager = ImageManager ( )
7471
7572 /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding.
7673 /// - Parameter url: The image url
@@ -86,11 +83,11 @@ public struct WebImage : View {
8683 context [ . animatedImageClass] = SDAnimatedImage . self
8784 }
8885 }
89- if #available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
90- _imageManager_SwiftUI = SwiftUI . StateObject ( wrappedValue : ImageManager ( url: url , options : options , context : context ) )
91- } else {
92- _imageManager_Backport = Backport . StateObject ( wrappedValue : ImageManager ( url : url , options : options , context: context ) )
93- }
86+ let imageModel = WebImageModel ( )
87+ imageModel . url = url
88+ imageModel . webOptions = options
89+ imageModel . webContext = context
90+ _imageModel = ObservedObject ( wrappedValue : imageModel )
9491 }
9592
9693 /// Create a web image with url, placeholder, custom options and context.
@@ -128,24 +125,24 @@ public struct WebImage : View {
128125 }
129126 } else {
130127 setupPlaceholder ( )
131- . onAppear {
128+ . onPlatformAppear ( appear : {
132129 self . imageManager. successBlock = self . imageHandler. successBlock
133130 self . imageManager. failureBlock = self . imageHandler. failureBlock
134131 self . imageManager. progressBlock = self . imageHandler. progressBlock
135132 // Load remote image when first appear
136- self . imageManager. load ( )
133+ self . imageManager. load ( url : imageModel . url , options : imageModel . webOptions , context : imageModel . webContext )
137134 guard self . imageConfiguration. retryOnAppear else { return }
138135 // When using prorgessive loading, the new partial image will cause onAppear. Filter this case
139136 if self . imageManager. image == nil && !self . imageManager. isIncremental {
140- self . imageManager. load ( )
137+ self . imageManager. load ( url : imageModel . url , options : imageModel . webOptions , context : imageModel . webContext )
141138 }
142- } . onDisappear {
139+ } , disappear : {
143140 guard self . imageConfiguration. cancelOnDisappear else { return }
144141 // When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case
145142 if self . imageManager. image == nil && !self . imageManager. isIncremental {
146143 self . imageManager. cancel ( )
147144 }
148- } . onReceive ( imageManager. objectWillChange) { _ in
145+ } ) . onReceive ( imageManager. objectWillChange) { _ in
149146 indicatorStatus. isLoading = imageManager. isLoading
150147 indicatorStatus. progress = imageManager. progress
151148 }
@@ -228,7 +225,7 @@ public struct WebImage : View {
228225 // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component
229226 if let placeholder = placeholder {
230227 // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :)
231- if imageManager . options . contains ( . delayPlaceholder) && imageManager. isLoading {
228+ if imageModel . webOptions . contains ( . delayPlaceholder) && imageManager. isLoading {
232229 return AnyView ( configure ( image: . empty) )
233230 } else {
234231 return placeholder
0 commit comments