I'm creating a viewmodel where I'm putting the data i retrieve from my database.
After retrieving the data, I map it and I create an array of a wrapper type.
The following is my view-model
class FoldersVM : ObservableObject {
let manager = PersistenceController()
@Published var folders: [FolderWrapper] = []
func retrieveFolders() {
folders = manager.retrieveFolders().map(FolderWrapper.init)
}
}
The folder wrapper is the following:
struct FolderWrapper: Hashable {
var folder: Folder
var name: String {
return folder.name ?? "error"
}
var index: Int {
return folder.index
}
}
Now, I want to add a "isSelected" variable to FolderWrapper and I want to be able to modify it by doing isSelected.toggle() in my View.
What I do in the view is the following:
struct FolderView1: View {
var folders: [FolderWrapper]
var body: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 10))]) {
ForEach(folders, id: \.self) { folder in
fw(folder: folder)
}
}
}
func fw(folder: FolderWrapper) -> some View{
Image(systemName: "folder")
.onTapGesture {
//Here I want to select but if I do i get an error saying that folder is immutable
}
}
}
So how can I create the isSelected variable and make it mutate in the view as well?
I tried making it binding but then i get an error saying the struct doesn't conform to hashable and Equatable. If i create a mutating method to change the variable then it says i can't call a mutating function on an immutable element (folder)
There are a number of ways you could add isSelected
to FolderWrapper
and
toggle it in a child view. I present two ways here, both
involve using @Binding var folders: [FolderWrapper]
struct FolderWrapper: Hashable {
var folder: Folder
var isSelected: Bool // <---
var name: String {
return folder.name ?? "error"
}
var index: Int {
return folder.index
}
}
struct FolderView1: View {
@Binding var folders: [FolderWrapper] // <---
var body: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 30))]) {
ForEach($folders, id: \.self) { $folder in // <--- $
fw(folder: $folder)
.foregroundColor(folder.isSelected ? .red : .blue)
}
}
}
func fw(folder: Binding<FolderWrapper>) -> some View{ // <---
Image(systemName: "folder")
.onTapGesture {
folder.wrappedValue.isSelected.toggle() // <---
}
}
}
struct FolderView2: View {
@Binding var folders: [FolderWrapper] // <---
var body: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 30))]) {
ForEach(folders.indices, id: \.self) { index in // <---
fw(index: index)
.foregroundColor(folders[index].isSelected ? .red : .blue)
}
}
}
func fw(index: Int) -> some View {
Image(systemName: "folder")
.onTapGesture {
folders[index].isSelected.toggle() // <---
}
}
}
struct ContentView: View {
@StateObject var vm = FoldersVM()
var body: some View {
FolderView1(folders: $vm.folders)
}
}