Make the ImageManager obseable
This commit is contained in:
parent
3d43d8ba29
commit
88e4deab48
|
@ -9,16 +9,21 @@
|
|||
import SwiftUI
|
||||
import SDWebImage
|
||||
|
||||
/// A Image observable object for handle image load process. This drive the Source of Truth for image loading status.
|
||||
/// You can use `@ObservedObject` to associate each instance of manager to your View type, which update your view's body from SwiftUI framework when image was loaded.
|
||||
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
|
||||
class ImageManager : ObservableObject, IndicatorReportable {
|
||||
@Published var image: PlatformImage? // loaded image, note when progressive loading, this will published multiple times with different partial image
|
||||
@Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
|
||||
@Published var progress: Double = 0 // network progress, should only be used for indicator binding
|
||||
public final class ImageManager : ObservableObject {
|
||||
/// loaded image, note when progressive loading, this will published multiple times with different partial image
|
||||
@Published public var image: PlatformImage?
|
||||
/// whether network is loading or cache is querying, should only be used for indicator binding
|
||||
@Published public var isLoading: Bool = false
|
||||
/// network progress, should only be used for indicator binding
|
||||
@Published public var progress: Double = 0
|
||||
/// true means during incremental loading
|
||||
@Published public var isIncremental: Bool = false
|
||||
|
||||
var manager: SDWebImageManager
|
||||
weak var currentOperation: SDWebImageOperation? = nil
|
||||
var isSuccess: Bool = false // true means request for this URL is ended forever, load() do nothing
|
||||
var isIncremental: Bool = false // true means during incremental loading
|
||||
var isFirstLoad: Bool = true // false after first call `load()`
|
||||
|
||||
var url: URL?
|
||||
|
@ -28,7 +33,11 @@ class ImageManager : ObservableObject, IndicatorReportable {
|
|||
var failureBlock: ((Error) -> Void)?
|
||||
var progressBlock: ((Int, Int) -> Void)?
|
||||
|
||||
init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
|
||||
/// Create a image manager for loading the specify url, with custom options and context.
|
||||
/// - Parameter url: The image url
|
||||
/// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values.
|
||||
/// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
|
||||
public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) {
|
||||
self.url = url
|
||||
self.options = options
|
||||
self.context = context
|
||||
|
@ -39,7 +48,8 @@ class ImageManager : ObservableObject, IndicatorReportable {
|
|||
}
|
||||
}
|
||||
|
||||
func load() {
|
||||
/// Start to load the url operation
|
||||
public func load() {
|
||||
isFirstLoad = false
|
||||
if currentOperation != nil {
|
||||
return
|
||||
|
@ -76,7 +86,6 @@ class ImageManager : ObservableObject, IndicatorReportable {
|
|||
self.isLoading = false
|
||||
self.progress = 1
|
||||
if let image = image {
|
||||
self.isSuccess = true
|
||||
self.successBlock?(image, cacheType)
|
||||
} else {
|
||||
self.failureBlock?(error ?? NSError())
|
||||
|
@ -85,9 +94,46 @@ class ImageManager : ObservableObject, IndicatorReportable {
|
|||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
currentOperation?.cancel()
|
||||
currentOperation = nil
|
||||
/// Cancel the current url loading
|
||||
public func cancel() {
|
||||
if let operation = currentOperation {
|
||||
operation.cancel()
|
||||
currentOperation = nil
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Completion Handler
|
||||
extension ImageManager {
|
||||
/// Provide the action when image load fails.
|
||||
/// - Parameters:
|
||||
/// - action: The action to perform. The first arg is the error during loading. If `action` is `nil`, the call has no effect.
|
||||
/// - Returns: A view that triggers `action` when this image load fails.
|
||||
public func onFailure(perform action: ((Error) -> Void)? = nil) -> ImageManager {
|
||||
self.failureBlock = action
|
||||
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 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, SDImageCacheType) -> Void)? = nil) -> ImageManager {
|
||||
self.successBlock = action
|
||||
return self
|
||||
}
|
||||
|
||||
/// Provide the action when image load progress changes.
|
||||
/// - Parameters:
|
||||
/// - action: The action to perform. The first arg is the received size, the second arg is the total size, all in bytes. If `action` is `nil`, the call has no effect.
|
||||
/// - Returns: A view that triggers `action` when this image load successes.
|
||||
public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> ImageManager {
|
||||
self.progressBlock = action
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// Indicator Reportor
|
||||
extension ImageManager: IndicatorReportable {}
|
||||
|
|
|
@ -120,14 +120,15 @@ public struct WebImage : View {
|
|||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
|
||||
.onAppear {
|
||||
guard self.retryOnAppear else { return }
|
||||
if !self.imageManager.isSuccess {
|
||||
// When using prorgessive loading, the new partial image will cause onAppear. Filter this case
|
||||
if self.imageManager.image == nil && !self.imageManager.isIncremental {
|
||||
self.imageManager.load()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
guard self.cancelOnDisappear else { return }
|
||||
// When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case
|
||||
if !self.imageManager.isSuccess && !self.imageManager.isIncremental {
|
||||
if self.imageManager.image == nil && !self.imageManager.isIncremental {
|
||||
self.imageManager.cancel()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue