Reputation: 677
I have a progress bar with its value updated while processing a file using the fileImporter modifier. I am not sure why the progress bar is not updated as the file is being processed (is it because it is part of the closure?). Below is how I am implementing the progress bar. Any help is greatly appreciated!
the ContentView basically has a button that triggers the processing of the selected file using a button and the .fileImporter modifier in a closure.
struct ContentView: View {
@ObservedObject var progress = ProgressItem()
@State var importData: Bool = false
var body: some View {
VStack {
Button(action: {importData = true}, label: {Text("Import Data")})
.fileImporter(isPresented: $importData, allowedContentTypes: [UTType.plainText], allowsMultipleSelection: false) { result in
do {
guard let selectedFile: URL = try result.get().first else { return }
let secured = selectedFile.startAccessingSecurityScopedResource()
DataManager().importData(fileURL: selectedFile)
if secured {selectedFile.stopAccessingSecurityScopedResource()}
} catch {
print(error.localizedDescription)
}
}
ProgressBar(value: $progress.progress).frame(height: 20)
}
}
}
The ProgressBar is a simple view that takes the progress value and updates it
struct ProgressBar: View {
@Binding var value: Double
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle().frame(width: geometry.size.width , height: geometry.size.height)
.opacity(0.3)
.foregroundColor(Color(UIColor.systemTeal))
Rectangle().frame(width: min(CGFloat(self.value)*geometry.size.width, geometry.size.width), height: geometry.size.height)
.foregroundColor(Color(UIColor.systemBlue))
.animation(.linear)
Text("\(value)")
}.cornerRadius(45.0)
}
}
}
I have a ProgressItem class which is an ObservableObject that contains the progress value:
class ProgressItem: ObservableObject {
@Published var progress: Double = 0
@Published var message: String = ""
}
The file is processed as part of the DataManager class. For the sake of this question, I have stripped out the processing details and just have a print statement to print out each line.Below is the DataManager class that updates the ProgressValue
class DataManager {
@Published var progressBar: ProgressItem? = nil
init() {
progressBar = ProgressItem()
}
func importData(fileURL: URL, delimeter: String = ",") {
let lines = try! String(contentsOf: fileURL, encoding: .utf8).components(separatedBy: .newlines).filter({!$0.isEmpty})
let numlines = lines.count
for index in 0..<numlines {
let line = String(lines[index])
print("\(line)")
progressBar?.progress = Double(index)/Double(numlines) * 100
}
}
}
Upvotes: 1
Views: 744
Reputation: 257729
I'd inject progress item in DataManager
via constructor
class DataManager {
private var progressBar: ProgressItem? = nil
init(progressBar: ProgressItem? = nil) {
self.progressBar = progressBar
}
func importData(fileURL: URL, delimeter: String = ",") {
// ... other code
// if importing on background queue
//DispatchQueue.main.async { // this better update async on main queue
progressBar?.progress = Double(index)/Double(numlines) * 100
//}
}
and then inject observed item from view into DataManager
, like
// ... other code
DataManager(progressBar: self.progress).importData(fileURL: selectedFile)
// ... other code
of course somewhere/somewhen you need to reset your current progress item. I don't see any completion in your code, but if you have one it might be inside it.
Upvotes: 1