Add the same overload method for onSuccess API, which introduce the image data arg. Keep the source code compatibility

This commit is contained in:
DreamPiggy 2020-05-06 23:06:39 +08:00
parent 8a347dc4dc
commit b174fee26b
4 changed files with 81 additions and 11 deletions

View File

@ -111,8 +111,9 @@ github "SDWebImage/SDWebImageSwiftUI"
var body: some View { var body: some View {
WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic")) WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"))
// Supports options and context, like `.delayPlaceholder` to show placeholder only when error // Supports options and context, like `.delayPlaceholder` to show placeholder only when error
.onSuccess { image, cacheType in .onSuccess { image, data, cacheType in
// Success // Success
// Note: Data exist only when queried from disk cache or network. Use `.queryMemoryData` if you really need data
} }
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
.placeholder(Image(systemName: "photo")) // Placeholder Image .placeholder(Image(systemName: "photo")) // Placeholder Image

View File

@ -49,7 +49,7 @@ final class AnimatedLoadingModel : ObservableObject, IndicatorReportable {
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
final class AnimatedImageHandler: ObservableObject { final class AnimatedImageHandler: ObservableObject {
// Completion Handler // Completion Handler
@Published var successBlock: ((PlatformImage, SDImageCacheType) -> Void)? @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
@Published var failureBlock: ((Error) -> Void)? @Published var failureBlock: ((Error) -> Void)?
@Published var progressBlock: ((Int, Int) -> Void)? @Published var progressBlock: ((Int, Int) -> Void)?
// Coordinator Handler // Coordinator Handler
@ -208,12 +208,15 @@ public struct AnimatedImage : PlatformViewRepresentable {
return return
} }
self.imageLoading.isLoading = true self.imageLoading.isLoading = true
if imageModel.webOptions.contains(.delayPlaceholder) { let options = imageModel.webOptions
if options.contains(.delayPlaceholder) {
self.imageConfiguration.placeholderView?.isHidden = true self.imageConfiguration.placeholderView?.isHidden = true
} else { } else {
self.imageConfiguration.placeholderView?.isHidden = false self.imageConfiguration.placeholderView?.isHidden = false
} }
view.wrapped.sd_setImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: imageModel.webOptions, context: imageModel.webContext, progress: { (receivedSize, expectedSize, _) in var context = imageModel.webContext ?? [:]
context[.animatedImageClass] = SDAnimatedImage.self
view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: options, context: context, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in
let progress: Double let progress: Double
if (expectedSize > 0) { if (expectedSize > 0) {
progress = Double(receivedSize) / Double(expectedSize) progress = Double(receivedSize) / Double(expectedSize)
@ -224,7 +227,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
self.imageLoading.progress = progress self.imageLoading.progress = progress
} }
self.imageHandler.progressBlock?(receivedSize, expectedSize) self.imageHandler.progressBlock?(receivedSize, expectedSize)
}) { (image, error, cacheType, _) in }) { (image, data, error, cacheType, finished, _) in
// This is a hack because of Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call // This is a hack because of Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call
// Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render) // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render)
if let hostingView = AnimatedImage.findHostingView(from: view) { if let hostingView = AnimatedImage.findHostingView(from: view) {
@ -241,7 +244,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
self.imageLoading.progress = 1 self.imageLoading.progress = 1
if let image = image { if let image = image {
self.imageConfiguration.placeholderView?.isHidden = true self.imageConfiguration.placeholderView?.isHidden = true
self.imageHandler.successBlock?(image, cacheType) self.imageHandler.successBlock?(image, data, cacheType)
} else { } else {
self.imageConfiguration.placeholderView?.isHidden = false self.imageConfiguration.placeholderView?.isHidden = false
self.imageHandler.failureBlock?(error ?? NSError()) self.imageHandler.failureBlock?(error ?? NSError())
@ -702,11 +705,33 @@ extension AnimatedImage {
return self return self
} }
/// Provide the action when image load successes.
/// - Parameters:
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
/// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: @escaping (PlatformImage) -> Void) -> AnimatedImage {
self.imageHandler.successBlock = { image, _, _ in
action(image)
}
return self
}
/// Provide the action when image load successes. /// Provide the action when image load successes.
/// - Parameters: /// - Parameters:
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect. /// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
/// - Returns: A view that triggers `action` when this image load successes. /// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> AnimatedImage { public func onSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) -> AnimatedImage {
self.imageHandler.successBlock = { image, _, cacheType in
action(image, cacheType)
}
return self
}
/// Provide the action when image load successes.
/// - Parameters:
/// - 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.
/// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> AnimatedImage {
self.imageHandler.successBlock = action self.imageHandler.successBlock = action
return self return self
} }

View File

@ -17,6 +17,8 @@ public final class ImageManager : ObservableObject {
@Published public var image: PlatformImage? @Published public var image: PlatformImage?
/// loaded image data, may be nil if hit from memory cache. This will only published once even on incremental image loading /// loaded image data, may be nil if hit from memory cache. This will only published once even on incremental image loading
@Published public var imageData: Data? @Published public var imageData: Data?
/// loaded image cache type, .none means from network
@Published public var cacheType: SDImageCacheType = .none
/// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason /// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason
@Published public var error: Error? @Published public var error: Error?
/// whether network is loading or cache is querying, should only be used for indicator binding /// whether network is loading or cache is querying, should only be used for indicator binding
@ -33,7 +35,7 @@ public final class ImageManager : ObservableObject {
var url: URL? var url: URL?
var options: SDWebImageOptions var options: SDWebImageOptions
var context: [SDWebImageContextOption : Any]? var context: [SDWebImageContextOption : Any]?
var successBlock: ((PlatformImage, SDImageCacheType) -> Void)? var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
var failureBlock: ((Error) -> Void)? var failureBlock: ((Error) -> Void)?
var progressBlock: ((Int, Int) -> Void)? var progressBlock: ((Int, Int) -> Void)?
@ -89,10 +91,11 @@ public final class ImageManager : ObservableObject {
self.isIncremental = !finished self.isIncremental = !finished
if finished { if finished {
self.imageData = data self.imageData = data
self.cacheType = cacheType
self.isLoading = false self.isLoading = false
self.progress = 1 self.progress = 1
if let image = image { if let image = image {
self.successBlock?(image, cacheType) self.successBlock?(image, data, cacheType)
} else { } else {
self.failureBlock?(error ?? NSError()) self.failureBlock?(error ?? NSError())
} }
@ -120,10 +123,28 @@ extension ImageManager {
self.failureBlock = action self.failureBlock = action
} }
/// Provide the action when image load successes.
/// - Parameters:
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
public func setOnSuccess(perform action: @escaping (PlatformImage) -> Void) {
self.successBlock = { image, _, _ in
action(image)
}
}
/// Provide the action when image load successes. /// Provide the action when image load successes.
/// - Parameters: /// - Parameters:
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect. /// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
public func setOnSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) { public func setOnSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) {
self.successBlock = { image, _, cacheType in
action(image, cacheType)
}
}
/// Provide the action when image load successes.
/// - Parameters:
/// - 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.
public func setOnSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) {
self.successBlock = action self.successBlock = action
} }

View File

@ -264,11 +264,34 @@ extension WebImage {
return self return self
} }
/// Provide the action when image load successes.
/// - Parameters:
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
/// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: @escaping (PlatformImage) -> Void) -> WebImage {
let action = action
self.imageManager.successBlock = { image, _, _ in
action(image)
}
return self
}
/// Provide the action when image load successes. /// Provide the action when image load successes.
/// - Parameters: /// - Parameters:
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect. /// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
/// - Returns: A view that triggers `action` when this image load successes. /// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> WebImage { public func onSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) -> WebImage {
self.imageManager.successBlock = { image, _, cacheType in
action(image, cacheType)
}
return self
}
/// Provide the action when image load successes.
/// - Parameters:
/// - 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.
/// - Returns: A view that triggers `action` when this image load successes.
public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> WebImage {
self.imageManager.successBlock = action self.imageManager.successBlock = action
return self return self
} }