Merge pull request #10 from SDWebImage/feature_on_success

Supports onSuccess/onFailure/onProgress callback for WebImage/AnimatedImage
This commit is contained in:
DreamPiggy 2019-10-03 18:07:05 +08:00 committed by GitHub
commit b1940dff46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 22 deletions

View File

@ -13,6 +13,7 @@
320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; };
320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */; };
320CDC3822FADB45007CF858 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */; };
321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; };
326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; };
CECA1658ECBAF54E3FF3EF58 /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A371F81C3B5BD6972F7A0E2 /* Pods_SDWebImageSwiftUIDemo.framework */; };
/* End PBXBuildFile section */
@ -28,6 +29,7 @@
320CDC3422FADB45007CF858 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
4A371F81C3B5BD6972F7A0E2 /* Pods_SDWebImageSwiftUIDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -70,6 +72,7 @@
320CDC2D22FADB44007CF858 /* SceneDelegate.swift */,
320CDC2F22FADB44007CF858 /* ContentView.swift */,
326B0D702345C01900D28269 /* DetailView.swift */,
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */,
320CDC3122FADB45007CF858 /* Assets.xcassets */,
320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */,
320CDC3922FADB45007CF858 /* Info.plist */,
@ -268,6 +271,7 @@
320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */,
326B0D712345C01900D28269 /* DetailView.swift in Sources */,
320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */,
321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */,
320CDC3022FADB44007CF858 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -12,18 +12,43 @@ import SDWebImageSwiftUI
struct DetailView: View {
let url: String
let animated: Bool
@State var progress: CGFloat = 1
var body: some View {
Group {
if animated {
AnimatedImage(url: URL(string:url), options: [.progressiveLoad])
.resizable()
.scaledToFit()
} else {
WebImage(url: URL(string:url), options: [.progressiveLoad])
.resizable()
.scaledToFit()
VStack {
HStack {
ProgressBar(value: $progress)
.foregroundColor(.blue)
.frame(maxHeight: 6)
}
Spacer()
HStack {
if animated {
AnimatedImage(url: URL(string:url), options: [.progressiveLoad])
.onProgress(perform: { (receivedSize, expectedSize) in
// SwiftUI engine itself ensure the main queue dispatch
if (expectedSize >= 0) {
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
} else {
self.progress = 1
}
})
.resizable()
.scaledToFit()
} else {
WebImage(url: URL(string:url), options: [.progressiveLoad])
.onProgress(perform: { (receivedSize, expectedSize) in
if (expectedSize >= 0) {
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
} else {
self.progress = 1
}
})
.resizable()
.scaledToFit()
}
}
Spacer()
}
}
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of the SDWebImage package.
* (c) DreamPiggy <lizhuoli1126@126.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import SwiftUI
/// A linear view that depicts the progress of a task over time.
public struct ProgressBar: View {
@Binding var value: CGFloat
public var body: some View {
GeometryReader { geometry in
ZStack(alignment: .topLeading) {
Capsule()
.frame(width: geometry.size.width)
.opacity(0.3)
Rectangle()
.frame(width: geometry.size.width * self.value)
}
}
.clipShape(Capsule())
.opacity(self.value < 1 ? 1 : 0)
}
}

View File

@ -15,6 +15,9 @@ import SDWebImage
final class AnimatedImageModel : ObservableObject {
@Published var image: PlatformImage?
@Published var url: URL?
@Published var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
@Published var failureBlock: ((Error) -> Void)?
@Published var progressBlock: ((Int, Int) -> Void)?
}
// Layout Binding Object
@ -67,7 +70,15 @@ public struct AnimatedImage : ViewRepresentable {
func updateView(_ view: AnimatedImageViewWrapper, context: ViewRepresentableContext<AnimatedImage>) {
view.wrapped.image = imageModel.image
if let url = imageModel.url {
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext)
view.wrapped.sd_setImage(with: url, placeholderImage: nil, options: webOptions, context: webContext, progress: { (receivedSize, expectedSize, _) in
self.imageModel.progressBlock?(receivedSize, expectedSize)
}) { (image, error, cacheType, _) in
if let image = image {
self.imageModel.successBlock?(image, cacheType)
} else {
self.imageModel.failureBlock?(error ?? NSError())
}
}
}
layoutView(view, context: context)
@ -178,17 +189,10 @@ public struct AnimatedImage : ViewRepresentable {
view.setNeedsDisplay()
#endif
}
public func image(_ image: PlatformImage?) -> Self {
imageModel.image = image
return self
}
public func imageUrl(_ url: URL?) -> Self {
imageModel.url = url
return self
}
}
// Layout
extension AnimatedImage {
public func resizable(
capInsets: EdgeInsets = EdgeInsets(),
resizingMode: Image.ResizingMode = .stretch) -> AnimatedImage
@ -236,6 +240,25 @@ public struct AnimatedImage : ViewRepresentable {
}
}
// Completion Handler
extension AnimatedImage {
public func onFailure(perform action: ((Error) -> Void)? = nil) -> AnimatedImage {
imageModel.failureBlock = action
return self
}
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> AnimatedImage {
imageModel.successBlock = action
return self
}
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> AnimatedImage {
imageModel.progressBlock = action
return self
}
}
// Initializer
extension AnimatedImage {
public init(url: URL?, placeholder: PlatformImage? = nil, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
self.webOptions = options

View File

@ -26,6 +26,9 @@ class ImageManager : ObservableObject {
var url: URL?
var options: SDWebImageOptions
var context: [SDWebImageContextOption : Any]?
var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
var failureBlock: ((Error) -> Void)?
var progressBlock: ((Int, Int) -> Void)?
init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
self.url = url
@ -34,9 +37,17 @@ class ImageManager : ObservableObject {
}
func load() {
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: nil) { (image, data, error, cacheType, _, _) in
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in
self?.progressBlock?(receivedSize, expectedSize)
}) { [weak self] (image, data, error, cacheType, _, _) in
guard let self = self else {
return
}
if let image = image {
self.image = image
self.successBlock?(image, cacheType)
} else {
self.failureBlock?(error ?? NSError())
}
}
}

View File

@ -55,6 +55,7 @@ public struct WebImage : View {
}
}
// Layout
extension WebImage {
func configure(_ block: @escaping (Image) -> Image) -> WebImage {
var result = self
@ -82,6 +83,23 @@ extension WebImage {
}
}
// Completion Handler
extension WebImage {
public func onFailure(perform action: ((Error) -> Void)? = nil) -> WebImage {
self.imageManager.failureBlock = action
return self
}
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> WebImage {
self.imageManager.successBlock = action
return self
}
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> WebImage {
self.imageManager.progressBlock = action
return self
}
}
#if DEBUG
struct WebImage_Previews : PreviewProvider {