首页 > 解决方案 > How to pass the value of progress in SwiftUI(maybe with combine)?

问题描述

I just started to learn SwiftUI, but did not have time to Combine. I don’t understand much about this. How to pass a value to the progress of downloading in ProgressView. how to configure ODRManager and how to transfer the value when downloading is in progress, not 0 and immediately 1, but 0.0 ... 0.1.0.2 ... 0.8,0.9,1.0 I would like to know different ways and means only SwiftUI, and using Combine or other methods.

Thanks a lot .

SwiftUI Code

import SwiftUI

struct ProgressView: View {

    @Binding var value: Float

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .leading) {
                Rectangle()
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .opacity(0.3)
                    .foregroundColor(.gray)

                Rectangle()
                    .frame(width: min(CGFloat(self.value) * geometry.size.width, geometry.size.width), height: geometry.size.height)
                    .foregroundColor(.blue)
                    .animation(.linear)
            }.cornerRadius(45.0)
        }
    }
}

struct ContentView: View {

    @State var progressValue: Float = 0.0

    var body: some View {
        VStack {
            Button(action: { //here is the function to download from ondemand }) {
                Text("Download")
                    .padding(.init(top: 10, leading: 30, bottom: 10, trailing: 30))
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(14)

            }
            .padding(.bottom, 30)

            ProgressView(value: $progressValue)
                .environmentObject(self.manager)
                .frame(width: 250, height: 20)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()

    }
}

OnDemand manager code

class ODRManager: NSObject {
    var resourceRequest : NSBundleResourceRequest?
    var progress:Float = 0.0 

    func fetchODRResourceWithName(fileName: String) {
        guard self.resourceRequest == nil else {return}
        self.resourceRequest = NSBundleResourceRequest(tags: [fileName])
        progress = Float((resourceRequest?.progress.fractionCompleted)!)
        self.resourceRequest!.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil)
        self.resourceRequest?.conditionallyBeginAccessingResources(completionHandler: { (resourceAvailable) in
            if !resourceAvailable {
                self.resourceRequest!.beginAccessingResources { err in
                    guard err == nil else {
                        print(err as Any)
                        return
                    }
                  print("downloading..")
                }
            } else { 
               print("was downloaded")
            }
        })
    }

    func stopDownloadingFile() {
        guard self.resourceRequest != nil else {
            return
        }
        self.resourceRequest!.endAccessingResources()
        self.resourceRequest!.progress.removeObserver(self, forKeyPath: "fractionCompleted")
        self.resourceRequest!.progress.cancel()
        self.resourceRequest = nil
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "fractionCompleted" {
            DispatchQueue.main.async { [weak self] in
                self.progress = Float((self?.resourceRequest!.progress.fractionCompleted)!)
            }
        }
    }
}

标签: swiftuikey-value-observingcombine

解决方案


目前尚不清楚 和 之间的关系ODRManagerContentView但我建议在它们之间引入一些中间模型(以避免紧密耦合),例如

class ProgressItem: ObservableObject {
    @Published var progressValue: Float = .zero
}

所以将相同的实例传递ProgressItem

class ODRManager: NSObject {
    var resourceRequest : NSBundleResourceRequest?
    var progress: ProgressItem? = nil // set up in init
    ...

struct ContentView: View {
    @ObservedObject var progress = ProgressItem() // as variant
    ...
        ProgressView(value: $progress.progressValue)
            .frame(width: 250, height: 20)

您只能有条件地加入引擎和 UI,而无需相互依赖。


推荐阅读