I am making a Slider based on a view model, but I am facing this error message Initializer 'init(value:in:step:label:minimumValueLabel:maximumValueLabel:onEditingChanged:)' requires that 'Int.Stride' (aka 'Int') conform to 'BinaryFloatingPoint'
It is strange because converting the integer from view model into Double doesn't quite do the trick.
I found very similar question and read the SO answer (How can I make Int conform to BinaryFloatingPoint or Double/CGFloat conform to BinaryInteger?), but it doesn't seem like I can implementation the solution for my case, probably because I am using ObservedObject
for the view model.
If I remove $
in front of setInformationVM.elapsedRestTime
, I would see another error message saying Cannot convert value of type 'Int' to expected argument type 'Binding<Int>'
They said "Binding are generally used when there is a need for 2-way communication" - would that mean the Slider needs a way to communicate/update back to the View Model? Why is it that the Slider was accepting @State private var xx: Double
for the value in general , but not a simple integer from my view model?
import Foundation
import SwiftUI
import Combine
struct SetRestDetailView: View {
@EnvironmentObject var watchDayProgramVM: WatchDayProgramViewModel
@State var showingLog = false
var body: some View {
GeometryReader { geometry in
ZStack() {
(view content removed for readability)
}
.sheet(isPresented: $showingLog) {
let setInformatationVM = self.watchDayProgramVM.exerciseVMList[0].sets[2]
setLoggingView(setInformationVM: setInformatationVM, suitability: 3, stepValue: 10)
}
}
}
setLoggingView
struct setLoggingView: View {
@Environment(\.dismiss) var dismiss
@ObservedObject var setInformationVM: SetInformationTestClass
@State var suitability: Int
var stepValue: Int
var body: some View {
GeometryReader { geometry in
let rect = geometry.frame(in: .global)
ScrollView {
VStack(spacing: 5) {
Text("Rested \(Int(setInformationVM.elapsedRestTime)) sec")
Slider(value: $setInformationVM.elapsedRestTime,
in: 0...setInformationVM.totalRestTime,
step: Int.Stride(stepValue),
label: {
Text("Slider")
}, minimumValueLabel: {
Text("-\(stepValue)")
}, maximumValueLabel: {
Text("+\(stepValue)")
})
.tint(Color.white)
.padding(.bottom)
Divider()
Spacer()
Text("suitability")
.frame(minWidth: 0, maxWidth: .infinity)
suitabilityStepper(rect: rect, maxsuitabilityness: 5, minsuitabilityness: 1, suitabilityIndex: restfullness)
Button(action: {
print("Update Button Pressed")
//TODO
//perform further actions to update suitability metric and elapsed rest time in the viewmodels before dismissing the view, and also update the iOS app by synching the view model.
dismiss()
}) {
HStack {
Text("Update")
.fontWeight(.medium)
}
}
.cornerRadius(40)
}
.border(Color.yellow)
}
}
}
SetInformationTestClass view model
class SetInformationTestClass: ObservableObject {
init(totalRestTime: Int, elapsedRestTime: Int, remainingRestTime: Int, isTimerRunning: Bool) {
self.totalRestTime = totalRestTime
self.elapsedRestTime = elapsedRestTime
self.remainingRestTime = remainingRestTime
}
@Published var totalRestTime: Int
@Published var elapsedRestTime: Int
@Published var remainingRestTime: Int
You can create a custom binding variable like :
let elapsedTime = Binding(
get: { Double(self.setInformationVM.elapsedRestTime) },
set: { self.setInformationVM.elapsedRestTime = Int($0) } // Or other custom logic
)
// then you reference it in the slider like:
Slider(elapsedTime, ...)