Replace ObservingDownloadStatsView with ObservingProgressIndicator

This more closely replicates the default look and feel of SwiftUI.ProgressView, but with explicit control over whether localizedAdditionalDescription is shown and without the label above the progress view that displays a fileOperationKind string.
This commit is contained in:
Brandon Evans 2021-02-18 19:10:12 -07:00
parent e638547900
commit 1469dfa56b
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
4 changed files with 41 additions and 99 deletions

View file

@ -102,7 +102,6 @@
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
E8F0838625D873A400A4C470 /* ObservingDownloadStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F0838525D873A400A4C470 /* ObservingDownloadStatsView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -265,7 +264,6 @@
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = "<group>"; };
E8F0838525D873A400A4C470 /* ObservingDownloadStatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingDownloadStatsView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -557,7 +555,6 @@
children = (
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */,
E8F0838525D873A400A4C470 /* ObservingDownloadStatsView.swift */,
);
path = InfoPane;
sourceTree = "<group>";
@ -797,7 +794,6 @@
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */,
CAC9F92D25BCDA4400B4965F /* HelperInstallState.swift in Sources */,
E8F0838625D873A400A4C470 /* ObservingDownloadStatsView.swift in Sources */,
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */,
CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */,
CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */,

View file

@ -8,16 +8,19 @@ import SwiftUI
public struct ObservingProgressIndicator: View {
let controlSize: NSControl.ControlSize
let style: NSProgressIndicator.Style
let showsAdditionalDescription: Bool
@StateObject private var progress: ProgressWrapper
public init(
_ progress: Progress,
controlSize: NSControl.ControlSize,
style: NSProgressIndicator.Style
style: NSProgressIndicator.Style,
showsAdditionalDescription: Bool = false
) {
_progress = StateObject(wrappedValue: ProgressWrapper(progress: progress))
self.controlSize = controlSize
self.style = style
self.showsAdditionalDescription = showsAdditionalDescription
}
class ProgressWrapper: ObservableObject {
@ -26,23 +29,31 @@ public struct ObservingProgressIndicator: View {
init(progress: Progress) {
self.progress = progress
cancellable = progress
.publisher(for: \.completedUnitCount)
.receive(on: RunLoop.main)
cancellable = progress.publisher(for: \.fractionCompleted)
.combineLatest(progress.publisher(for: \.localizedAdditionalDescription))
.throttle(for: 1.0, scheduler: DispatchQueue.main, latest: true)
.sink { [weak self] _ in self?.objectWillChange.send() }
}
}
public var body: some View {
ProgressIndicator(
minValue: 0.0,
maxValue: 1.0,
doubleValue: progress.progress.fractionCompleted,
controlSize: controlSize,
isIndeterminate: progress.progress.isIndeterminate,
style: style
)
.help("Downloading: \(Int((progress.progress.fractionCompleted * 100)))% complete")
VStack(alignment: .leading, spacing: 0) {
ProgressIndicator(
minValue: 0.0,
maxValue: 1.0,
doubleValue: progress.progress.fractionCompleted,
controlSize: controlSize,
isIndeterminate: progress.progress.isIndeterminate,
style: style
)
.help("Downloading: \(Int((progress.progress.fractionCompleted * 100)))% complete")
if showsAdditionalDescription, progress.progress.localizedAdditionalDescription.isEmpty == false {
Text(progress.progress.localizedAdditionalDescription)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
@ -57,6 +68,20 @@ struct ObservingProgressBar_Previews: PreviewProvider {
controlSize: .small,
style: .spinning
)
ObservingProgressIndicator(
configure(Progress()) {
$0.kind = .file
$0.fileOperationKind = .downloading
$0.estimatedTimeRemaining = 123
$0.totalUnitCount = 11944848484
$0.completedUnitCount = 848444920
$0.throughput = 9211681
},
controlSize: .regular,
style: .bar,
showsAdditionalDescription: true
)
}
.previewLayout(.sizeThatFits)
}

View file

@ -9,10 +9,11 @@ struct InstallationStepDetailView: View {
case let .downloading(progress):
Text("Step \(installationStep.stepNumber) of \(installationStep.stepCount): \(installationStep.message)")
.font(.title2)
ObservingDownloadStatsView(
ObservingProgressIndicator(
progress,
controlSize: .regular,
style: .bar
style: .bar,
showsAdditionalDescription: true
)
case .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing:

View file

@ -1,80 +0,0 @@
import Combine
import SwiftUI
/// A ProgressIndicator that reflects the state of a Progress object.
/// This functionality is already built in to ProgressView,
/// but this implementation ensures that changes are received on the main thread.
@available(iOS 14.0, macOS 11.0, *)
public struct ObservingDownloadStatsView: View {
let controlSize: NSControl.ControlSize
let style: NSProgressIndicator.Style
@StateObject private var progress: ProgressWrapper
public init(
_ progress: Progress,
controlSize: NSControl.ControlSize,
style: NSProgressIndicator.Style
) {
_progress = StateObject(wrappedValue: ProgressWrapper(progress: progress))
self.controlSize = controlSize
self.style = style
}
class ProgressWrapper: ObservableObject {
var progress: Progress
var cancellable: AnyCancellable!
init(progress: Progress) {
self.progress = progress
cancellable = progress
.publisher(for: \.completedUnitCount)
.receive(on: RunLoop.main)
.sink { [weak self] _ in self?.objectWillChange.send() }
}
}
public var body: some View {
VStack{
HStack {
ProgressIndicator(
minValue: 0.0,
maxValue: 1.0,
doubleValue: progress.progress.fractionCompleted,
controlSize: controlSize,
isIndeterminate: progress.progress.isIndeterminate,
style: style
)
.help("Downloading: \(Int((progress.progress.fractionCompleted * 100)))% complete")
Text("\(Int((progress.progress.fractionCompleted * 100)))%")
}
HStack {
if let fileCompletedCount = progress.progress.fileCompletedCount, let fileTotalCount = progress.progress.fileTotalCount {
Text("\(ByteCountFormatter.string(fromByteCount: Int64(fileCompletedCount), countStyle: .file)) of \(ByteCountFormatter.string(fromByteCount: Int64(fileTotalCount), countStyle: .file))")
}
if let throughput = progress.progress.throughput {
Text(" at \(ByteCountFormatter.string(fromByteCount: Int64(throughput), countStyle: .binary))/sec")
}
}
}
}
}
@available(iOS 14.0, macOS 11.0, *)
struct ObservingDownloadStats_Previews: PreviewProvider {
static var previews: some View {
Group {
ObservingDownloadStatsView(
configure(Progress(totalUnitCount: 100)) {
$0.completedUnitCount = 40; $0.throughput = 9211681; $0.fileCompletedCount = 84844492; $0.fileTotalCount = 11944848484
},
controlSize: .small,
style: .bar
)
}
.previewLayout(.sizeThatFits)
}
}