Home > Net >  How to implement a two-way binding in a ForEach statement with nested views with Swift?
How to implement a two-way binding in a ForEach statement with nested views with Swift?

Time:04-27

I've implemented a two-way binding in a Foreach statement that seems to work properly:

struct ContentView: View {
    @ObservedObject var dataManager: ContentViewModel
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false){
            HStack(spacing: 20){
                ForEach($dataManager.data) { $item in 
                    if item.status == true {
                        Button(item.tag){
                            item.status.toggle()
                        }
                        .foregroundColor(.white)
                        .background(Color.red)
                    } else {
                        Button(item.tag){
                            item.status.toggle()
                        }
                        .foregroundColor(.white)
                        .background(Color.blue)
                    }
                }
            }
        }
    }
}

The problem is when I've to use some nested views like:

struct ContentView: View {
    @ObservedObject var dataManager: ContentViewModel
    var body: some View {
        
        ScrollView(.horizontal, showsIndicators: false){
            HStack(spacing: 20){
                ForEach($dataManager.data) { $item in
                    if item.status == true {
                        DetailView(dataManager: dataManager, item: item)
                            .foregroundColor(.white)
                            .background(Color.red)
                    } else {
                        Button(item.tag){
                            item.status.toggle()
                        }
                        .foregroundColor(.white)
                        .background(Color.blue)
                    }
                }
            }
        }
    }
}

and:

struct DetailView: View {
    
    @ObservedObject var dataManager: ContentViewModel
    var item: SMTTagBoardItem
    
    var body: some View {
        Button(item.tag){
            item.status.toggle()
        }
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(dataManager: ContentViewModel(), item: SMTTagBoardItem(id: UUID(), tag: "tag1", rank: 0, status: true))
    }
}

I receive this error:

Cannot use mutating member on immutable value: 'self' is immutable

at line :

item.status.toggle() 

How can I implement a two-way binding in a ForEach statement that works by passing values to nested views?

CodePudding user response:

First of all, instead of using @ObservedObject, use EnvironmentObject and StateObject.

Then instead of var item use @Binding var item. (I think dataManager.data is @Published variable)

ContentView

struct ContentView: View {
    @StateObject var dataManager: ContentViewModel
    var body: some View {
   
        ScrollView(.horizontal, showsIndicators: false){
            HStack(spacing: 20){
                ForEach($dataManager.data) { $item in
                    if item.status == true {
                        DetailView(item: $item)
                            .environmentObject(dataManager)
                            .foregroundColor(.white)
                            .background(Color.red)
                    } else {
                        //more code
                    }
                }
            }
        }
    }
}

DetailView

struct DetailView: View {
    
    @EnvironmentObject var dataManager: ContentViewModel
    @Binding var item: SMTTagBoardItem
    
    var body: some View {
        Button(item.tag){
            item.status.toggle()
        }
    }
}
  • Related