How to bind a Float value from a model to a @Binding:Float property in a SwiftUI View

Advertisements

I have a Circular ProgressBar View that takes a @Binding var progress: Float property as its progress value, what I would like to be able to do is pass the var progress: Float value from the Task objects respectably in the List. I tried assigning the progress = task.progress the value from the Task objects to a @State property within the ContentView but of course that cannot be done inside a View (see commented code line below).

How can I pass the progress values from each Task to the ProgressBar view respectably?

Task Model:

class Task:Identifiable{
    var name = ""
    var progress: Float = 0.0
    
    init(name:String, progress:Float){
        self.name = name
        self.progress = progress
    }
}

Content View

var tasks = [Task(name: "Ketchen Floors", progress: 0.5),
            Task(name: "Install Windows", progress: 0.75)]

struct ContentView: View {
    @State var progress:Float = 0.15
    
    var body: some View {
        List {
            ForEach(tasks) { task in
                // outputs error: Type '()' cannot conform to 'View'
                // progress = task.progress 
                HStack{
                    Text(task.name)
                    // here $progress value shoud come from task.progress
                    ProgressBar(progress: $progress)
                }
            }
        }
    }
}

ProgressBar View

struct ProgressBar: View {
    @Binding var progress: Float
    
    var body: some View {
        ZStack {
            Circle()
                .stroke(lineWidth:5.0)
                .opacity(0.3)
                .foregroundColor(Color.orange)
            
            Circle()
                .trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
                .stroke(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round))
                .foregroundColor(Color.orange)
                .rotationEffect(Angle(degrees: 270.0))
                .animation(.linear, value: progress)

            VStack{
                Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
                    .font(.caption2)
            }
        }
    }
}

Screen

As you can see, every item shows the initial 15% progress value, no surprise. Again, what I want is to be able to use the value from each Task respectably, Ketchen Floors at 50% and Install Windows at 75%.

>Solution :

First, add your array inside ContentView and make it @State

After that, you can directly bind var by task.

struct ContentView: View {
    @State var progress:Float = 0.15
    
    @State var tasks = [Task(name: "Ketchen Floors", progress: 0.5),
                 Task(name: "Install Windows", progress: 0.75)] // <---- Here
    
    var body: some View {
        List {
            ForEach($tasks) { $task in // <---- Here
                // outputs error: Type '()' cannot conform to 'View'
                // progress = task.progress
                HStack{
                    Text(task.name)
                    // here $progress value shoud come from task.progress
                    ProgressBar(progress: $task.progress) // <---- Here
                }
            }
        }
    }
}

Leave a ReplyCancel reply