I am using an array of Label-Views to display a ScrollView with all the Labels. When those Labels are tapped, they are selected and an @State
boolean property in the Label-View is toggled. So at the bottom when a "Submit" Button is clicked, the values of the selected Labels are sent to a server.
In the "Submit" Button I am using a For-Loop to loop over the Label-Views and check for Labels that have myLabel.isSelected == true
, but all of them are returning false
even after being selected.
The main View simplified:
struct Sheet: View {
@State var myLabels: [MyLabel] = [
MyLabel(systemImage: "sparkles", text: "Keine Gebrauchsspuren"),
MyLabel(systemImage: "checkmark.circle", text: "Minimale Gebrauchsspuren"),
MyLabel(systemImage: "exclamationmark.circle", text: "Mehrere Gebrauchsspuren"),
MyLabel(systemImage: "xmark.circle", text: "Starke Gebrauchsspuren"),
MyLabel(systemImage: "arrow.down.to.line", text: "Delle(n)")
]
var body: some View {
ScrollView {
ForEach(myLabels, id: \.self) {label in label}
}
Button {
var combinedString = ""
for label in myLabels {
if label.isSelected {
combinedString = label.text
}
}
postTextFunc(combinedString)
} label: {
Text("Submit")
}
}
The combinedString
is always returning empty because when I iterate over the myLabels
all the Labels have isSelected
set to false
. Is this because of how structs are handled by SwiftUI when their properties are changed? And how would I be able to reach my goal of returning the values of only the tipped Labels?
Much appreciated!
CodePudding user response:
Views are structs in SwiftUI. So passing them into a function will create a copy of those Views. And you are itterating over the original Views that have not been modified.
I think a ViewModel/Model would be best here.
struct Model: Identifiable{
var selected: Bool
let text: String
let id = UUID()
}
class Viewmodel: ObservableObject{
@Published var models: [Model] = [] //Create the datamodel here
func select(_ selectedModel: Model){
var selectedModel = selectedModel
selectedModel.selected.toggle() //Create a new copy and toggle selection
// get index of selected item
guard let index = models.firstIndex(where: {$0.id == selectedModel.id}) else{
return
}
// assign item. This will toggle changes in the view
models[index] = selectedModel
}
func post(){
//Create combined string
let combinedString = models.filter{$0.selected}.reduce("") { partialResult, model in
partialResult model.text
}
//call post
postTextFunc(combinedString)
}
func postTextFunc(_ string: String){
}
}
struct InnerView: View{
@EnvironmentObject private var viewmodel: Viewmodel
var selected: Model // provide the individual data here
var body: some View{
Text(selected.text)
.onTapGesture {
viewmodel.select(selected)
}
}
}
struct TestView: View{
@StateObject private var viewmodel = Viewmodel()
var body: some View {
ScrollView {
ForEach(viewmodel.models, id: \.id) { model in
InnerView(selected: model)
.environmentObject(viewmodel) // pass viewmodel in environment
}
}
Button {
viewmodel.post()
} label: {
Text("Submit")
}
}
}