Skip to content

Commit b1940df

Browse files
authored
Merge pull request #10 from SDWebImage/feature_on_success
Supports onSuccess/onFailure/onProgress callback for WebImage/AnimatedImage
2 parents 5606897 + f5b6d1e commit b1940df

File tree

6 files changed

+131
-22
lines changed

6 files changed

+131
-22
lines changed

Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; };
1414
320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */; };
1515
320CDC3822FADB45007CF858 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */; };
16+
321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; };
1617
326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; };
1718
CECA1658ECBAF54E3FF3EF58 /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A371F81C3B5BD6972F7A0E2 /* Pods_SDWebImageSwiftUIDemo.framework */; };
1819
/* End PBXBuildFile section */
@@ -28,6 +29,7 @@
2829
320CDC3422FADB45007CF858 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
2930
320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
3031
320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
32+
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
3133
326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
3234
3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
3335
4A371F81C3B5BD6972F7A0E2 /* Pods_SDWebImageSwiftUIDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -70,6 +72,7 @@
7072
320CDC2D22FADB44007CF858 /* SceneDelegate.swift */,
7173
320CDC2F22FADB44007CF858 /* ContentView.swift */,
7274
326B0D702345C01900D28269 /* DetailView.swift */,
75+
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */,
7376
320CDC3122FADB45007CF858 /* Assets.xcassets */,
7477
320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */,
7578
320CDC3922FADB45007CF858 /* Info.plist */,
@@ -268,6 +271,7 @@
268271
320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */,
269272
326B0D712345C01900D28269 /* DetailView.swift in Sources */,
270273
320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */,
274+
321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */,
271275
320CDC3022FADB44007CF858 /* ContentView.swift in Sources */,
272276
);
273277
runOnlyForDeploymentPostprocessing = 0;

Example/SDWebImageSwiftUIDemo/DetailView.swift

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,43 @@ import SDWebImageSwiftUI
1212
struct DetailView: View {
1313
let url: String
1414
let animated: Bool
15+
@State var progress: CGFloat = 1
1516

1617
var body: some View {
17-
Group {
18-
if animated {
19-
AnimatedImage(url: URL(string:url), options: [.progressiveLoad])
20-
.resizable()
21-
.scaledToFit()
22-
} else {
23-
WebImage(url: URL(string:url), options: [.progressiveLoad])
24-
.resizable()
25-
.scaledToFit()
18+
VStack {
19+
HStack {
20+
ProgressBar(value: $progress)
21+
.foregroundColor(.blue)
22+
.frame(maxHeight: 6)
2623
}
24+
Spacer()
25+
HStack {
26+
if animated {
27+
AnimatedImage(url: URL(string:url), options: [.progressiveLoad])
28+
.onProgress(perform: { (receivedSize, expectedSize) in
29+
// SwiftUI engine itself ensure the main queue dispatch
30+
if (expectedSize >= 0) {
31+
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
32+
} else {
33+
self.progress = 1
34+
}
35+
})
36+
.resizable()
37+
.scaledToFit()
38+
} else {
39+
WebImage(url: URL(string:url), options: [.progressiveLoad])
40+
.onProgress(perform: { (receivedSize, expectedSize) in
41+
if (expectedSize >= 0) {
42+
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
43+
} else {
44+
self.progress = 1
45+
}
46+
})
47+
.resizable()
48+
.scaledToFit()
49+
}
50+
}
51+
Spacer()
2752
}
2853
}
2954
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* This file is part of the SDWebImage package.
3+
* (c) DreamPiggy <lizhuoli1126@126.com>
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*/
8+
9+
import SwiftUI
10+
11+
/// A linear view that depicts the progress of a task over time.
12+
public struct ProgressBar: View {
13+
@Binding var value: CGFloat
14+
15+
public var body: some View {
16+
GeometryReader { geometry in
17+
ZStack(alignment: .topLeading) {
18+
Capsule()
19+
.frame(width: geometry.size.width)
20+
.opacity(0.3)
21+
Rectangle()
22+
.frame(width: geometry.size.width * self.value)
23+
}
24+
}
25+
.clipShape(Capsule())
26+
.opacity(self.value < 1 ? 1 : 0)
27+
}
28+
}

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import SDWebImage
1515
final class AnimatedImageModel : ObservableObject {
1616
@Published var image: PlatformImage?
1717
@Published var url: URL?
18+
@Published var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
19+
@Published var failureBlock: ((Error) -> Void)?
20+
@Published var progressBlock: ((Int, Int) -> Void)?
1821
}
1922

2023
// Layout Binding Object
@@ -67,7 +70,15 @@ public struct AnimatedImage : ViewRepresentable {
6770
func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
6871
view.wrapped.image = imageModel.image
6972
if let url = imageModel.url {
70-
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext)
73+
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in
74+
self.imageModel.progressBlock?(receivedSize, expectedSize)
75+
}) { (image, error, cacheType, _) in
76+
if let image = image {
77+
self.imageModel.successBlock?(image, cacheType)
78+
} else {
79+
self.imageModel.failureBlock?(error ?? NSError())
80+
}
81+
}
7182
}
7283

7384
layoutView(view, context: context)
@@ -178,17 +189,10 @@ public struct AnimatedImage : ViewRepresentable {
178189
view.setNeedsDisplay()
179190
#endif
180191
}
181-
182-
public func image(_ image: PlatformImage?) -> Self {
183-
imageModel.image = image
184-
return self
185-
}
186-
187-
public func imageUrl(_ url: URL?) -> Self {
188-
imageModel.url = url
189-
return self
190-
}
191-
192+
}
193+
194+
// Layout
195+
extension AnimatedImage {
192196
public func resizable(
193197
capInsets: EdgeInsets = EdgeInsets(),
194198
resizingMode: Image.ResizingMode = .stretch) -> AnimatedImage
@@ -236,6 +240,25 @@ public struct AnimatedImage : ViewRepresentable {
236240
}
237241
}
238242

243+
// Completion Handler
244+
extension AnimatedImage {
245+
public func onFailure(perform action: ((Error) -> Void)? = nil) -> AnimatedImage {
246+
imageModel.failureBlock = action
247+
return self
248+
}
249+
250+
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> AnimatedImage {
251+
imageModel.successBlock = action
252+
return self
253+
}
254+
255+
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> AnimatedImage {
256+
imageModel.progressBlock = action
257+
return self
258+
}
259+
}
260+
261+
// Initializer
239262
extension AnimatedImage {
240263
public init(url: URL?, placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
241264
self.webOptions = options

SDWebImageSwiftUI/Classes/ImageManager.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class ImageManager : ObservableObject {
2626
var url: URL?
2727
var options: SDWebImageOptions
2828
var context: [SDWebImageContextOption : Any]?
29+
var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
30+
var failureBlock: ((Error) -> Void)?
31+
var progressBlock: ((Int, Int) -> Void)?
2932

3033
init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
3134
self.url = url
@@ -34,9 +37,17 @@ class ImageManager : ObservableObject {
3437
}
3538

3639
func load() {
37-
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: nil) { (image, data, error, cacheType, _, _) in
40+
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in
41+
self?.progressBlock?(receivedSize, expectedSize)
42+
}) { [weak self] (image, data, error, cacheType, _, _) in
43+
guard let self = self else {
44+
return
45+
}
3846
if let image = image {
3947
self.image = image
48+
self.successBlock?(image, cacheType)
49+
} else {
50+
self.failureBlock?(error ?? NSError())
4051
}
4152
}
4253
}

SDWebImageSwiftUI/Classes/WebImage.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public struct WebImage : View {
5555
}
5656
}
5757

58+
// Layout
5859
extension WebImage {
5960
func configure(_ block: @escaping (Image) -> Image) -> WebImage {
6061
var result = self
@@ -82,6 +83,23 @@ extension WebImage {
8283
}
8384
}
8485

86+
// Completion Handler
87+
extension WebImage {
88+
public func onFailure(perform action: ((Error) -> Void)? = nil) -> WebImage {
89+
self.imageManager.failureBlock = action
90+
return self
91+
}
92+
93+
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> WebImage {
94+
self.imageManager.successBlock = action
95+
return self
96+
}
97+
98+
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> WebImage {
99+
self.imageManager.progressBlock = action
100+
return self
101+
}
102+
}
85103

86104
#if DEBUG
87105
struct WebImage_Previews : PreviewProvider {

0 commit comments

Comments
 (0)