Add activity indicator implementation as well, using UIProgressView/NSProgressIndicator

This commit is contained in:
DreamPiggy 2019-10-25 22:55:19 +08:00
parent 6a2eb0264a
commit 40c5644569
7 changed files with 133 additions and 10 deletions

View File

@ -65,12 +65,8 @@ struct DetailView: View {
.scaledToFit()
} else {
WebImage(url: URL(string:url), options: [.progressiveLoad])
.onProgress { receivedSize, expectedSize in
if (expectedSize > 0) {
self.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
} else {
self.progress = 1
}
.indicator { isAnimating, progress in
ProgressIndicator(isAnimating, progress: progress)
}
.resizable()
.scaledToFit()

View File

@ -23,6 +23,10 @@
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 */; };
326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; };
326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; };
326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; };
326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.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 */; };
@ -109,6 +113,7 @@
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>"; };
326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.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>"; };
@ -176,6 +181,7 @@
children = (
326B84812363350C0011BDFB /* Indicator.swift */,
326B8486236335110011BDFB /* ActivityIndicator.swift */,
326B848B236335400011BDFB /* ProgressIndicator.swift */,
);
path = Indicator;
sourceTree = "<group>";
@ -438,6 +444,7 @@
buildActionMask = 2147483647;
files = (
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84822363350C0011BDFB /* Indicator.swift in Sources */,
32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
@ -453,6 +460,7 @@
buildActionMask = 2147483647;
files = (
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84832363350C0011BDFB /* Indicator.swift in Sources */,
32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
@ -468,6 +476,7 @@
buildActionMask = 2147483647;
files = (
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84842363350C0011BDFB /* Indicator.swift in Sources */,
32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,
@ -483,6 +492,7 @@
buildActionMask = 2147483647;
files = (
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */,
326B84852363350C0011BDFB /* Indicator.swift in Sources */,
32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */,
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */,

View File

@ -39,7 +39,6 @@ class ImageManager : ObservableObject {
guard let self = self else {
return
}
self.progressBlock?(receivedSize, expectedSize)
let progress: CGFloat
if (expectedSize > 0) {
progress = CGFloat(receivedSize) / CGFloat(expectedSize)
@ -49,6 +48,7 @@ class ImageManager : ObservableObject {
DispatchQueue.main.async {
self.progress = progress
}
self.progressBlock?(receivedSize, expectedSize)
}) { [weak self] (image, data, error, cacheType, finished, _) in
guard let self = self else {
return
@ -58,6 +58,7 @@ class ImageManager : ObservableObject {
}
if finished {
self.isLoading = false
self.progress = 1
if let image = image {
self.successBlock?(image, cacheType)
} else {

View File

@ -11,7 +11,7 @@ import SDWebImage
#if !os(watchOS)
// View Wrapper
/// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug)
public class AnimatedImageViewWrapper : PlatformView {
var wrapped = SDAnimatedImageView()
var interpolationQuality = CGInterpolationQuality.default
@ -54,4 +54,36 @@ public class AnimatedImageViewWrapper : PlatformView {
}
}
/// Use wrapper to solve the `UIProgressView`/`NSProgressIndicator` frame origin NaN crash (SwiftUI's bug)
public class ProgressIndicatorWrapper : PlatformView {
#if os(macOS)
var wrapped = NSProgressIndicator()
#else
var wrapped = UIProgressView(progressViewStyle: .default)
#endif
#if os(macOS)
public override func layout() {
super.layout()
wrapped.frame = self.bounds
}
#else
public override func layoutSubviews() {
super.layoutSubviews()
wrapped.frame = self.bounds
wrapped.center = self.center
}
#endif
public override init(frame frameRect: CGRect) {
super.init(frame: frameRect)
addSubview(wrapped)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
addSubview(wrapped)
}
}
#endif

View File

@ -6,13 +6,13 @@
* file that was distributed with this source code.
*/
import Swift
import SwiftUI
/// An activity indicator (system style)
public struct ActivityIndicator: PlatformViewRepresentable {
@Binding var isAnimating: Bool
public init(_ isAnimating: Binding<Bool> = .constant(true)) {
public init(_ isAnimating: Binding<Bool>) {
self._isAnimating = isAnimating
}

View File

@ -9,6 +9,7 @@
import Foundation
import SwiftUI
/// A container view to hold the indicator builder
public struct Indicator : View {
var builder: (Binding<Bool>, Binding<CGFloat>) -> AnyView
public typealias Body = Never

View File

@ -0,0 +1,83 @@
/*
* 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 progress bar indicator (system style)
public struct ProgressIndicator: PlatformViewRepresentable {
@Binding var isAnimating: Bool
@Binding var progress: CGFloat
public init(_ isAnimating: Binding<Bool>, progress: Binding<CGFloat>) {
self._isAnimating = isAnimating
self._progress = progress
}
#if os(macOS)
public typealias NSViewType = ProgressIndicatorWrapper
#elseif os(iOS) || os(tvOS)
public typealias UIViewType = ProgressIndicatorWrapper
#endif
#if os(iOS) || os(tvOS)
public func makeUIView(context: UIViewRepresentableContext<ProgressIndicator>) -> ProgressIndicatorWrapper {
let uiView = ProgressIndicatorWrapper()
let view = uiView.wrapped
view.progressViewStyle = .default
return uiView
}
public func updateUIView(_ uiView: ProgressIndicatorWrapper, context: UIViewRepresentableContext<ProgressIndicator>) {
let view = uiView.wrapped
if isAnimating {
view.setProgress(Float(progress), animated: true)
} else {
if progress == 0 {
view.isHidden = false
view.progress = 0
} else {
view.isHidden = true
view.progress = 1
}
}
}
#endif
#if os(macOS)
public func makeNSView(context: NSViewRepresentableContext<ProgressIndicator>) -> ProgressIndicatorWrapper {
let nsView = ProgressIndicatorWrapper()
let view = nsView.wrapped
view.style = .bar
view.isDisplayedWhenStopped = false
view.controlSize = .small
return nsView
}
public func updateNSView(_ nsView: ProgressIndicatorWrapper, context: NSViewRepresentableContext<ProgressIndicator>) {
let view = nsView.wrapped
if isAnimating {
view.isIndeterminate = false
view.doubleValue = Double(progress) * 100
view.startAnimation(nil)
} else {
if progress == 0 {
view.isHidden = false
view.isIndeterminate = true
view.doubleValue = 0
view.stopAnimation(nil)
} else {
view.isHidden = true
view.isIndeterminate = false
view.doubleValue = 100
view.stopAnimation(nil)
}
}
}
#endif
}