Home > front end >  SwiftUI Stepper not working when bound to a model property directly
SwiftUI Stepper not working when bound to a model property directly

Time:11-05

I am fairly new to iOS development. I am trying to update the property "cars" in the "Sire" model using a stepper. Whenever I press on or - from the stepper controls, it changes the value by a step and then becomes disabled.

If I bind the stepper to the variable cars, it works flawlessly.

struct AddSireView: View {
//    @EnvironmentObject var sireVM:SiresViewModel
    @State var newSire = Sire (id:"", name: "", ownerID: 0, info:"", achievments: "", cars: 0, cups: 0)
    @State var cars = 0
    @State var cups = 0
    @State private var state = FormState.idle
    
    var createAction: CreateAction
    
    // TODO: Put validation that the added sire is valid and if not show errors to the user

    var body: some View {

            Form {
                VStack (spacing: 18) {
                    TitledTextView(text: $newSire.name, placeHolder: "الاسم", title: "الاسم")
                    
                    TiltedTextEditor(text: Binding<String>($newSire.info)!, title: "معلومات البعير")
                    TiltedTextEditor(text: Binding<String>($newSire.achievments)!, title: "انجازات البعير")
                }
                
                Stepper(value: $newSire.cars, in: 0...10,step:1) {
                    HStack {
                        Text ("سيارات:")
                        TextField("Cars", value: $newSire.cars, formatter: NumberFormatter.decimal)
                    }
                }

enter image description here

And this is the "Sire" struct

struct Sire: Hashable, Identifiable, Decodable  {
    static func == (lhs: Sire, rhs: Sire) -> Bool {
        lhs.id == rhs.id && lhs.name == rhs.name && lhs.ownerID == rhs.ownerID
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
        hasher.combine(ownerID)

    }
     var id:String
     var name:String
     var ownerID:Int
     var fatherID:String?
     var info:String?
     var achievments:String?
     var cars:Int = 0
     var cups:Int = 0
    
     
    init (id:String, name:String, ownerID:Int, info:String? = nil, achievments:String? = nil,
          fatherID:String? = nil, cars:Int = 0, cups:Int = 0) {
         self.id = id
         self.name = name
         self.ownerID = ownerID
         self.cars = cars
         self.cups = cups
        self.info = info
        self.achievments = achievments
     }  
     
}

"Sire" was a class and i made it a Struct thinking that that was the problem, but to no avail.

CodePudding user response:

Consider this approach using an ObservableObject to hold your Sire. This allows you to use both the Stepper and the Textfield at the same time.

 struct ContentView: View {
     @StateObject var sireModel = SireModel() // <-- here
     
     var body: some View {
         Form {
             Stepper(value: $sireModel.sire.cars, in: 0...10, step:1) {
                 HStack {
                     Text ("سيارات: ")
                     TextField("", value: $sireModel.sire.cars, formatter: NumberFormatter())
                 }
             }
         }
     }
 }

 class SireModel: ObservableObject {
     @Published var sire: Sire = Sire(id:"", name: "", ownerID: 0, info:"", achievments: "", cars: 0, cups: 0)
 }

CodePudding user response:

Get rid of the custom implementations for Equatable and Hashable (func == and func hash) you don't include cars in it so SwiftUI doesn't know when to reload.

SwiftUI is all about identity if you change how Swift computes the identity (using Hashable, Equatable and Identifiable) you change the behavior.

Check out Demystify SwiftUI

The video above is the "best" place to learn about the concept.

  • Related