Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass binding to subview with SwiftUI when the variable is nested in an object?

Tags:

swift

swiftui

This works

import SwiftUI
struct ContentView : View {
    @State var val1: Int = 0
    var body: some View {
        MySubview(val1: $val1)
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView(val1: 0)
    }
}
#endif

struct MySubview : View {
    @Binding var val1: Int
    var body: some View {
        return Text("Value = \(val1)")
    }
}

But when the variable is nested in an Object, this fails

import SwiftUI
struct MyStruct {
    let number: Int
}

struct ContentView : View {
    @State var val1 = MyStruct(number: 7)
    var body: some View {
        MySubview(val1: $val1.number)
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView(val1: 0)
    }
}
#endif

struct MySubview : View {
    @Binding var val1: Int
    var body: some View {
        return Text("Value = \(val1)")
    }
}

Error shown: Generic parameter 'Subject' could not be inferred

How do i pass nested variable as a binding to a subview?

like image 754
Just a coder Avatar asked Jun 24 '19 09:06

Just a coder


2 Answers

The error is very misleading. Number must be a var, not a let:

struct MyStruct {
    var number: Int
}

Change it and it will work fine.

like image 80
kontiki Avatar answered Sep 21 '22 12:09

kontiki


Your code was good except for needing var number: Int as kontiki pointed out.

To help with understanding of passing bindings about between views I prepared the following code which shows use of @Binding in slightly different ways:

import SwiftUI

struct Zoo    { var shed:    Shed     }
struct Shed   { var animals: [Animal] }
struct Animal { var legs:    Int      }

struct ZooView : View {
  @State var zoo = Zoo( shed: Shed(animals:
      [ Animal(legs: 2), Animal(legs: 4) ] ) )

  var body: some View {
    VStack {
      Text("Legs in the zoo directly:")
      Text("Animal 1 Legs: \(zoo.shed.animals[0].legs)")
      Text("Animal 2 Legs: \(zoo.shed.animals[1].legs)")
      Divider()
      Text("And now with nested views:")
      ShedView(shed: $zoo.shed)
    }
  }
}

struct ShedView : View {
  @Binding var shed: Shed

  var body: some View {
    ForEach(shed.animals.indices) { index in
      VStack {
        Text("Animal: \(index+1)")
        AnimalView(animal: self.$shed.animals[index])
      }
    }
  }
}

struct AnimalView : View {
  @Binding var animal: Animal

  var body: some View {
    VStack {
      Text("Legs = \(animal.legs)")
      Button(
      action: { self.animal.legs += 1 }) {
        Text("Another leg")
      }
    }
  }
}

In particular ShedView is given a binding to a shed and it looks up an animal in the array of animals in the shed and passes a binding to the animal on to AnimalView.

like image 38
Cortado-J Avatar answered Sep 18 '22 12:09

Cortado-J