【问题标题】:Update progress value from fileImporter modifier in SwiftUI从 SwiftUI 中的 fileImporter 修饰符更新进度值
【发布时间】:2020-10-15 16:02:08
【问题描述】:

我有一个进度条,在使用 fileImporter 修饰符处理文件时更新了它的值。我不确定为什么在处理文件时进度条没有更新(是因为它是关闭的一部分吗?)。下面是我如何实现进度条。非常感谢任何帮助!

ContentView 基本上有一个按钮,它使用按钮和闭包中的 .fileImporter 修饰符触发对所选文件的处理。

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)
    }
}

}

ProgressBar 是一个简单的视图,它获取进度值并更新它

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)
    }
}

}

我有一个 ProgressItem 类,它是一个包含进度值的 ObservableObject:

class ProgressItem: ObservableObject {
    @Published var progress: Double = 0
    @Published var message: String = ""
}

文件作为 DataManager 类的一部分进行处理。为了这个问题,我已经剥离了处理细节,只是有一个打印语句来打印出每一行。下面是更新ProgressValue的DataManager类

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
        }
    }
}

【问题讨论】:

    标签: ios swift swiftui progress-bar


    【解决方案1】:

    我会通过构造函数在DataManager 中注入进度项

    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
            //}
        }
    
    

    然后将观察到的项目从视图中注入DataManager,例如

        // ... other code
    
        DataManager(progressBar: self.progress).importData(fileURL: selectedFile)
    
        // ... other code
    

    当然在某处/某处您需要重置当前进度项。我在你的代码中没有看到任何完成,但如果你有一个,它可能在里面。

    【讨论】:

    • 感谢您的回复!当我尝试实现它时,更新会等到读取文件后更新进度条。本质上,DispatchQueue.main.async 代码块一直在等待读取完成并在事后更新。有什么建议吗?
    • 可能你在主队列上做了所有事情,尝试删除它,但如果是这样,那么你的 UI 将不会随着进度更新,因为导入本身会阻塞 UI。
    • 是的,一切都在主队列中。但后来我将文件处理更改为 DispatchQueue.global().async。进行此更改会更新 progressBar,但完成后不会触发 UI 更新。
    • 谢谢!。我让它工作。我不得不将 ui 更新放在另一个 main.async 队列中。
    猜你喜欢
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2019-12-29
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多