0

How to access values from particular item on the list made with ForEach? As you can see I was trying something like this (and many other options):

Text(item[2].onOff ? "On" : "Off")

I wanted to check the value of toggle of 2nd list item (for example) and update text on the screen saying if it's on or off.

And I know that it's something to do with @Binding and I was searching examples of this and trying few things, but I cannot make it to work. Maybe it is a beginner question. I would appreciate if someone could help me.

My ContentView:

struct ContentView: View { // @Binding var onOff : Bool @State private var onOff = false @State private var test = false var body: some View { NavigationView { List { HStack { Text("Is 2nd item on or off? ") Text(onOff ? "On" : "Off") // Text(item[2].onOff ? "On" : "Off") } ForEach((1...15), id: \.self) {item in ListItemView() } } .navigationBarTitle(Text("List")) } } } 

And ListItemView:

import SwiftUI struct ListItemView: View { @State private var onOff : Bool = false // @Binding var onOff : Bool var body: some View { HStack { Text("99") .font(.title) Text("List item") Spacer() Toggle(isOn: self.$onOff) { Text("Label") } .labelsHidden() } } } 
3
  • 1
    The appropriate approach would be to have model as ObservableObject and pass it between views, so each could modify related properties, and you could use them navigating between views. (@State actually is designed to be view-only-thing). Commented Nov 21, 2019 at 14:03
  • Isn’t @Binding designed for this as well? I read somewhere that ObservableObject is for many views. But binding is if I want to pass values of variables between just 2 views. I will read more ObservableObject. Maybe I misunderstood something :) Commented Nov 21, 2019 at 14:06
  • @State is the binding just for one view. Commented Nov 21, 2019 at 14:28

3 Answers 3

1

I don't know what exactly you would like to achieve, but I made you a working example:

struct ListItemView: View { @ObservedObject var model: ListItemModel var body: some View { HStack { Text("99") .font(.title) Text("List item") Spacer() Toggle(isOn: self.$model.switchedOnOff) { Text("Label") } .labelsHidden() } } } class ListItemModel: ObservableObject { @Published var switchedOnOff: Bool = false } struct ContentView: View { @State private var onOff = false @State private var test = false @State private var list = [ (id: 0, model: ListItemModel()), (id: 1, model: ListItemModel()), (id: 2, model: ListItemModel()), (id: 3, model: ListItemModel()), (id: 4, model: ListItemModel()) ] var body: some View { NavigationView { List { HStack { Text("Is 2nd item on or off? ") Text(onOff ? "On" : "Off") // Text(item[2].onOff ? "On" : "Off") } ForEach(self.list, id: \.id) {item in ListItemView(model: item.model) } } .navigationBarTitle(Text("List")) }.onReceive(self.list[1].model.$switchedOnOff, perform: { switchedOnOff_second_item in self.onOff = switchedOnOff_second_item }) } } 

The @Published basically creates a Publisher, which the UI can listen to per onReceive().

Play around with this and you will figure out what these things do!

Good luck :)

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks @krjw, it works. I see that you created an array. I think this is good solution for final product, where I assume I would build an array from CoreData for example. But... For a purpose of building a fast prototype, is the here a way of achieving similar effect using ForEach((1...15), id: \.self) {item in? How would I access particular value using id? I was trying something like Text(self.item[2].$onOff ? "On" : "Off") but failed completely
I don’t think it is possible to do it since you need the objects available in ContentView. You cannot access it except you create a binding to every child.
Yes, that's the problem. I don't know how to use binding with all the rows using ForEach((1...15). You can see my example below. Thanks for your feedback. I am learning a lot. Maybe my attempts are a dead end, but at least I will learn how not to do it :)
1
import SwiftUI struct ContentView: View { @State private var onOffList = Array(repeating: true, count: 15) var body: some View { NavigationView { List { HStack { Text("Is 2nd item on or off? ") Text(onOffList[1] ? "On" : "Off") } ForEach((onOffList.indices), id: \.self) {idx in ListItemView(onOff: self.$onOffList[idx]) } } .navigationBarTitle(Text("List")) } } } struct ListItemView: View { @Binding var onOff : Bool var body: some View { HStack { Text("99") .font(.title) Text("List item") Spacer() Toggle(isOn: $onOff) { Text("Label") } .labelsHidden() } } } 

Comments

0

I understand that you are directing me to use ObservableObject. And probably it's the best way to go with final product. But I am still thinking about @Binding as I just need to pass values better between 2 views only. Maybe I still don't understand binding, but I came to this solution.

struct ContentView: View { // @Binding var onOff : Bool @State private var onOff = false // @State private var test = false var body: some View { NavigationView { List { HStack { Text("Is 2nd item on or off? ") Text(onOff ? "On" : "Off") // Text(self.item[2].$onOff ? "On" : "Off") // Text(item[2].onOff ? "On" : "Off") } ForEach((1...15), id: \.self) {item in ListItemView(onOff: self.$onOff) } } .navigationBarTitle(Text("List")) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } 

and ListItemView:

import SwiftUI struct ListItemView: View { // @State private var onOff : Bool = false @Binding var onOff : Bool var body: some View { HStack { Text("99") .font(.title) Text("List item") Spacer() Toggle(isOn: self.$onOff) { Text("Label") } .labelsHidden() } } } 

What is happening now is text is being updated after I tap toggle. But I have 2 problems:

  • tapping 1 toggle changes all of them. I think it's because of this line:

    ListItemView(onOff: self.$onOff)

  • I still cannot access value of just one row. In my understanding ForEach((1...15), id: .self) make each row have their own id, but I don't know how to access it later on.

1 Comment

Thats why I save the id in an array with a model that holds the variable of the switch. You can also move the HStack into the ForEach loop and have a variable per id

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.