Skip to content

Commit cd187c8

Browse files
committed
Added the support for AnimatedImage to use @ViewBuilder placeholder, better than UIImage for common customizable placeholders
1 parent 7458f13 commit cd187c8

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ final class AnimatedImageConfiguration: ObservableObject {
8383
var indicator: SDWebImageIndicator?
8484
var transition: SDWebImageTransition?
8585
var placeholder: PlatformImage?
86+
var placeholderView: PlatformView? {
87+
didSet {
88+
oldValue?.removeFromSuperview()
89+
}
90+
}
8691
}
8792

8893
/// A Image View type to load image from url, data or bundle. Supports animated and static image format.
@@ -203,6 +208,11 @@ public struct AnimatedImage : PlatformViewRepresentable {
203208
return
204209
}
205210
self.imageLoading.isLoading = true
211+
if imageModel.webOptions.contains(.delayPlaceholder) {
212+
self.imageConfiguration.placeholderView?.isHidden = true
213+
} else {
214+
self.imageConfiguration.placeholderView?.isHidden = false
215+
}
206216
view.wrapped.sd_setImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: imageModel.webOptions, context: imageModel.webContext, progress: { (receivedSize, expectedSize, _) in
207217
let progress: Double
208218
if (expectedSize > 0) {
@@ -230,8 +240,10 @@ public struct AnimatedImage : PlatformViewRepresentable {
230240
self.imageLoading.isLoading = false
231241
self.imageLoading.progress = 1
232242
if let image = image {
243+
self.imageConfiguration.placeholderView?.isHidden = true
233244
self.imageHandler.successBlock?(image, cacheType)
234245
} else {
246+
self.imageConfiguration.placeholderView?.isHidden = false
235247
self.imageHandler.failureBlock?(error ?? NSError())
236248
}
237249
}
@@ -263,6 +275,21 @@ public struct AnimatedImage : PlatformViewRepresentable {
263275
} else if let url = imageModel.url, url != view.wrapped.sd_imageURL {
264276
view.wrapped.sd_imageIndicator = imageConfiguration.indicator
265277
view.wrapped.sd_imageTransition = imageConfiguration.transition
278+
if let placeholderView = imageConfiguration.placeholderView {
279+
placeholderView.removeFromSuperview()
280+
placeholderView.isHidden = true
281+
// Placeholder View should below the Indicator View
282+
if let indicatorView = imageConfiguration.indicator?.indicatorView {
283+
#if os(macOS)
284+
view.wrapped.addSubview(placeholderView, positioned: .below, relativeTo: indicatorView)
285+
#else
286+
view.wrapped.insertSubview(placeholderView, belowSubview: indicatorView)
287+
#endif
288+
} else {
289+
view.wrapped.addSubview(placeholderView)
290+
}
291+
placeholderView.bindFrameToSuperviewBounds()
292+
}
266293
loadImage(view, context: context)
267294
}
268295

@@ -728,8 +755,21 @@ extension AnimatedImage {
728755

729756
/// Associate a placeholder when loading image with url
730757
/// - Parameter content: A view that describes the placeholder.
731-
public func placeholder(_ placeholder: PlatformImage?) -> AnimatedImage {
732-
self.imageConfiguration.placeholder = placeholder
758+
/// - note: The differences between this and placeholder image, it's that placeholder image replace the image for image view, but this modify the View Hierarchy to overlay the placeholder hosting view
759+
public func placeholder<T>(@ViewBuilder content: () -> T) -> AnimatedImage where T : View {
760+
#if os(macOS)
761+
let hostingView = NSHostingView(rootView: content())
762+
#else
763+
let hostingView = _UIHostingView(rootView: content())
764+
#endif
765+
self.imageConfiguration.placeholderView = hostingView
766+
return self
767+
}
768+
769+
/// Associate a placeholder image when loading image with url
770+
/// - Parameter content: A view that describes the placeholder.
771+
public func placeholder(_ image: PlatformImage?) -> AnimatedImage {
772+
self.imageConfiguration.placeholder = image
733773
return self
734774
}
735775

SDWebImageSwiftUI/Classes/ImageViewWrapper.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,21 @@ public class ProgressIndicatorWrapper : PlatformView {
122122
addSubview(wrapped)
123123
}
124124
}
125+
extension PlatformView {
126+
/// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
127+
/// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
128+
func bindFrameToSuperviewBounds() {
129+
guard let superview = self.superview else {
130+
print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
131+
return
132+
}
133+
134+
self.translatesAutoresizingMaskIntoConstraints = false
135+
self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
136+
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
137+
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
138+
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true
139+
}
140+
}
125141

126142
#endif

0 commit comments

Comments
 (0)