Add support for WebImage to use indicator. Using protocol and struct based solution, and with SwiftUI Binding for isAnimating and progress
This commit is contained in:
parent
cbe282b69c
commit
6a2eb0264a
|
@ -11,7 +11,7 @@ PODS:
|
|||
- SDWebImage (5.2.3):
|
||||
- SDWebImage/Core (= 5.2.3)
|
||||
- SDWebImage/Core (5.2.3)
|
||||
- SDWebImageSwiftUI (0.4.1):
|
||||
- SDWebImageSwiftUI (0.4.2):
|
||||
- SDWebImage (~> 5.1)
|
||||
- SDWebImageWebPCoder (0.2.5):
|
||||
- libwebp (~> 1.0)
|
||||
|
@ -34,7 +34,7 @@ EXTERNAL SOURCES:
|
|||
SPEC CHECKSUMS:
|
||||
libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e
|
||||
SDWebImage: 46a7f73228f84ce80990c786e4372cf4db5875ce
|
||||
SDWebImageSwiftUI: 15eeed7470ba9cd64fa7e8dddd62e12df58d07f3
|
||||
SDWebImageSwiftUI: b91be76ecb0cdf74c18f6cd92aae8f19a9ded02d
|
||||
SDWebImageWebPCoder: 947093edd1349d820c40afbd9f42acb6cdecd987
|
||||
|
||||
PODFILE CHECKSUM: 3fb06a5173225e197f3a4bf2be7e5586a693257a
|
||||
|
|
|
@ -81,11 +81,16 @@ struct ContentView: View {
|
|||
HStack {
|
||||
if self.animated {
|
||||
AnimatedImage(url: URL(string:url))
|
||||
.indicator(SDWebImageActivityIndicator.medium)
|
||||
.transition(.fade)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
||||
} else {
|
||||
WebImage(url: URL(string:url))
|
||||
.indicator { isAnimating, _ in
|
||||
ActivityIndicator(isAnimating)
|
||||
}
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
324F61CC235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
324F61CD235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
324F61CE235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */; };
|
||||
326B84822363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; };
|
||||
326B84832363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; };
|
||||
326B84842363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; };
|
||||
326B84852363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; };
|
||||
326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; };
|
||||
326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; };
|
||||
326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; };
|
||||
326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; };
|
||||
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
|
@ -99,6 +107,8 @@
|
|||
/* Begin PBXFileReference section */
|
||||
324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAnimatedImageInterface.h; sourceTree = "<group>"; };
|
||||
324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageInterface.m; sourceTree = "<group>"; };
|
||||
326B84812363350C0011BDFB /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Indicator.swift; sourceTree = "<group>"; };
|
||||
326B8486236335110011BDFB /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
|
||||
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = "<group>"; };
|
||||
32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -161,6 +171,15 @@
|
|||
path = ObjC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
326099472362E09E006EBB22 /* Indicator */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
326B84812363350C0011BDFB /* Indicator.swift */,
|
||||
326B8486236335110011BDFB /* ActivityIndicator.swift */,
|
||||
);
|
||||
path = Indicator;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C43DC222FD540D00BE87F5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -194,6 +213,7 @@
|
|||
32C43DDB22FD54C600BE87F5 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
326099472362E09E006EBB22 /* Indicator */,
|
||||
324F61C4235E07EC003973B8 /* ObjC */,
|
||||
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */,
|
||||
32C43DDE22FD54C600BE87F5 /* WebImage.swift */,
|
||||
|
@ -418,8 +438,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
326B84822363350C0011BDFB /* Indicator.swift in Sources */,
|
||||
32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */,
|
||||
32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CB235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
|
@ -431,8 +453,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
326B84832363350C0011BDFB /* Indicator.swift in Sources */,
|
||||
32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */,
|
||||
32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CC235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
|
@ -444,8 +468,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
326B84842363350C0011BDFB /* Indicator.swift in Sources */,
|
||||
32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */,
|
||||
32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CD235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
|
@ -457,8 +483,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
326B84852363350C0011BDFB /* Indicator.swift in Sources */,
|
||||
32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
|
||||
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
|
||||
326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */,
|
||||
32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */,
|
||||
32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */,
|
||||
324F61CE235E07EC003973B8 /* SDAnimatedImageInterface.m in Sources */,
|
||||
|
|
|
@ -11,6 +11,8 @@ import SDWebImage
|
|||
|
||||
class ImageManager : ObservableObject {
|
||||
@Published var image: PlatformImage?
|
||||
@Published var isLoading: Bool = false
|
||||
@Published var progress: CGFloat = 0
|
||||
|
||||
var manager = SDWebImageManager.shared
|
||||
weak var currentOperation: SDWebImageOperation? = nil
|
||||
|
@ -32,8 +34,21 @@ class ImageManager : ObservableObject {
|
|||
if currentOperation != nil {
|
||||
return
|
||||
}
|
||||
self.isLoading = true
|
||||
currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in
|
||||
self?.progressBlock?(receivedSize, expectedSize)
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.progressBlock?(receivedSize, expectedSize)
|
||||
let progress: CGFloat
|
||||
if (expectedSize > 0) {
|
||||
progress = CGFloat(receivedSize) / CGFloat(expectedSize)
|
||||
} else {
|
||||
progress = 0
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.progress = progress
|
||||
}
|
||||
}) { [weak self] (image, data, error, cacheType, finished, _) in
|
||||
guard let self = self else {
|
||||
return
|
||||
|
@ -42,6 +57,7 @@ class ImageManager : ObservableObject {
|
|||
self.image = image
|
||||
}
|
||||
if finished {
|
||||
self.isLoading = false
|
||||
if let image = image {
|
||||
self.successBlock?(image, cacheType)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 Swift
|
||||
import SwiftUI
|
||||
|
||||
public struct ActivityIndicator: PlatformViewRepresentable {
|
||||
@Binding var isAnimating: Bool
|
||||
|
||||
public init(_ isAnimating: Binding<Bool> = .constant(true)) {
|
||||
self._isAnimating = isAnimating
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
public typealias NSViewType = NSProgressIndicator
|
||||
#elseif os(iOS) || os(tvOS)
|
||||
public typealias UIViewType = UIActivityIndicatorView
|
||||
#endif
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
public func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
|
||||
let indicator = UIActivityIndicatorView(style: .medium)
|
||||
indicator.hidesWhenStopped = true
|
||||
return indicator
|
||||
}
|
||||
|
||||
public func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
|
||||
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
public func makeNSView(context: NSViewRepresentableContext<ActivityIndicator>) -> NSProgressIndicator {
|
||||
let indicator = NSProgressIndicator()
|
||||
indicator.style = .spinning
|
||||
indicator.isDisplayedWhenStopped = false
|
||||
return indicator
|
||||
}
|
||||
|
||||
public func updateNSView(_ nsView: NSProgressIndicator, context: NSViewRepresentableContext<ActivityIndicator>) {
|
||||
isAnimating ? nsView.startAnimation(nil) : nsView.stopAnimation(nil)
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 Foundation
|
||||
import SwiftUI
|
||||
|
||||
public struct Indicator : View {
|
||||
var builder: (Binding<Bool>, Binding<CGFloat>) -> AnyView
|
||||
public typealias Body = Never
|
||||
public var body: Never {
|
||||
fatalError()
|
||||
}
|
||||
public init<T>(@ViewBuilder builder: @escaping (_ isAnimating: Binding<Bool>, _ progress: Binding<CGFloat>) -> T) where T : View {
|
||||
self.builder = { isAnimating, progress in
|
||||
AnyView(builder(isAnimating, progress))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,8 +16,11 @@ public struct WebImage : View {
|
|||
var context: [SDWebImageContextOption : Any]?
|
||||
|
||||
var configurations: [(Image) -> Image] = []
|
||||
var indicator: Indicator?
|
||||
|
||||
@ObservedObject var imageManager: ImageManager
|
||||
@State var progress: CGFloat = 0
|
||||
@State var isLoading: Bool = false
|
||||
|
||||
/// Create a web image with url, placeholder, custom options and context.
|
||||
/// - Parameter url: The image url
|
||||
|
@ -46,7 +49,7 @@ public struct WebImage : View {
|
|||
// this can ensure we load the image, SDWebImage take care of the duplicated query
|
||||
self.imageManager.load()
|
||||
}
|
||||
return configurations.reduce(image) { (previous, configuration) in
|
||||
let view = configurations.reduce(image) { (previous, configuration) in
|
||||
configuration(previous)
|
||||
}
|
||||
.onAppear {
|
||||
|
@ -57,6 +60,19 @@ public struct WebImage : View {
|
|||
.onDisappear {
|
||||
self.imageManager.cancel()
|
||||
}
|
||||
// Convert Combine.Publisher to Binding, I think this need a better API from Apple :)
|
||||
.onReceive(imageManager.$isLoading) { self.isLoading = $0 }
|
||||
.onReceive(imageManager.$progress) { self.progress = $0 }
|
||||
if let indicator = indicator {
|
||||
return AnyView(
|
||||
ZStack {
|
||||
view
|
||||
indicator.builder($isLoading, $progress)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return AnyView(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +144,19 @@ extension WebImage {
|
|||
}
|
||||
}
|
||||
|
||||
extension WebImage {
|
||||
|
||||
/// Associate a indicator when loading image with url
|
||||
/// - Parameter builder: builder description
|
||||
/// - Parameter isAnimating: A Binding to control the animation. If image is loading, the value is true, else false.
|
||||
/// - Parameter progress: A Binding to control the progress during loading. If no progress can be reported, the value is 0.
|
||||
public func indicator<T>(_ builder: @escaping (_ isAnimating: Binding<Bool>, _ progress: Binding<CGFloat>) -> T) -> WebImage where T : View {
|
||||
var result = self
|
||||
result.indicator = Indicator(builder: builder)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct WebImage_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
|
Loading…
Reference in New Issue