Home > OS >  How to make shortcut/binding to a struct inside class? (SwiftUI)
How to make shortcut/binding to a struct inside class? (SwiftUI)

Time:10-01

In my class, I have an array of Item and an optional var selection, which is supposed to store a SHORTCUT to the selected Item. I need to be able to access the selected Item by referring to selection.

In order for selection to work as SHORTCUT does selection has to be a Binding? If yes, is it a @Binding like in structs, or maybe Binding<T>? And does it has to be @Published?

My code:

import SwiftUI

struct Item: Identifiable, Equatable {
    var id = UUID().uuidString
    var color: Color
}

class Model: ObservableObject {
    @Published var items: [Item] = [Item(color: .blue), Item(color: .blue), Item(color: .blue)]
    @Published var selection: Item? //this supposed to be not a value, but a SHORTCUT to a selected item inside array
    
    func setSelection (item: Item) {
        selection = item
    }
    
    func changeColor (color: Color) {
        if selection != nil {
            selection?.color = color// << PROBLEM is that it only copies object and modifies the copy instead of original
        }
    }
}

struct ContentView: View {
    @StateObject var model = Model()
    var body: some View {
        //list
        VStack {
            ForEach(model.items.indices, id:\.hashValue) { i in
                SubView(item: $model.items[i], model: model)
            }
            // change color button
            Button {
                model.changeColor(color: .red)
            } label: {Text("Make Selection Red")}
            
        }.padding()
    }
}

struct SubView: View {
    @Binding var item: Item
    var model: Model
    
    var body: some View {
        VStack {
            // button which sets selection to an items inside this subview
            Button {
                model.setSelection(item: item)
            } label: {
                Text("Select").background(item.color)}.buttonStyle(PlainButtonStyle())
        }
    }
}

Desired functionality: click on one if items, and then charging its color.

CodePudding user response:

since you want selection to be "....a selected item inside array", then you could just use the index in the array of items. Something like this: (although your code logic is a bit strange to me, I assumed this is just a test example)

struct Item: Identifiable, Equatable {
    var id = UUID().uuidString
    var color: Color
}

class Model: ObservableObject {
    @Published var items: [Item] = [Item(color: .blue), Item(color: .blue), Item(color: .blue)]
    @Published var selection: Int?   // <-- here

    func changeColor(color: Color) {
        if let ndx = selection {     // <-- here
            items[ndx].color = color
        }
    }
}

struct ContentView: View {
    @StateObject var model = Model()
    var body: some View {
        //list
        VStack {
            ForEach(model.items.indices, id:\.self) { i in
                SubView(index: i, model: model)  // <-- here
            }
            // change color button
            Button {
                model.changeColor(color: .red)
            } label: {Text("Make Selection Red")}

        }.padding()
    }
}

struct SubView: View {
    var index: Int   // <-- here
    @ObservedObject var model: Model  // <-- here

    var body: some View {
        VStack {
            // button which sets selection to an items inside this subview
            Button {
                model.selection = index
            } label: {
                Text("Select").background(model.items[index].color)  // <-- here
            }
            .buttonStyle(PlainButtonStyle())
        }
    }
}
  • Related