swiftswiftuixcode15ios17lazyvgrid

Update values of only selected GridItem SwiftUI


I activate a .contextMenu by selecting a GridItem in a LazyVGrid. Then change a Bool value in just that selected GridItem to update its contents.

My issue is after tapping the Button in the .contextMenu and changing the Bool. The changes are shown in all GridItem not just selected.

How. to just reflect changes in selected GridItem?

import SwiftUI
    
    struct GridItemView: View {
        
        var id : Int
        @Binding var isRed : Bool
        
        var body: some View {
            VStack {
                Text("Hello")
                Image(systemName: "globe.europe.africa")
            }
            .foregroundStyle(isRed ? .red : .black)
            .padding()
            .border(.black)
        }
    }

import SwiftUI

struct ContentView: View {
    
    let gridLayout = Array(repeating: GridItem(.flexible(), spacing: 0), count: 4)
    let numberOfGridItems = 1092
    
    @State private var isRed : Bool = false
    
    
    var body: some View {
        VStack {
            ScrollView(.vertical) {
                LazyVGrid(columns: gridLayout){
                    ForEach(0..<numberOfGridItems, id: \.self){ gridItemIndex in
                       let myGridItem = GridItemView(id: gridItemIndex, isRed: $isRed)
                        myGridItem.contextMenu{
                            Button(action: {
                                myGridItem.isRed.toggle()
                            },
                                   label: {
                                Label("Toggle isRed for gridItemIndex \(myGridItem.id)", systemImage: "paintbrush.fill")
                                
                            })
                        }
                        
                    }
                }.scrollTargetLayout()
            }.scrollTargetBehavior(.viewAligned)
        }
    }
}

Outcome Vs Expected Outcome and Expected Outcome

**Sorry 1st image should have shown all items as Red


Solution

  • I was able to find a solution from This Post

    rather than having a @Binding to a Bool which was setting the Bool value of all my GridItems not just those which were selected. I instead added the gridItemIndex to an Array @State private var isSelected : [Int] = [] and moved the.contextMenu (Though have changed to an overlay) to perform the actions from being created inside the ForEach where I create my LazyVGrid to the GridItemView itself and handle the changes there.

    struct ContentView: View {
        
        let gridLayout = Array(repeating: GridItem(.flexible(), spacing: 0), count: 3)
        let numberOfGridItems = 9//1092
        
        
        @State private var isSelected : [Int] = []
        
        var body: some View {
            VStack {
                ScrollView(.vertical) {
                    LazyVGrid(columns: gridLayout){
                        ForEach(0..<numberOfGridItems, id: \.self){ gridItemIndex in
                            GridItemView(gridItemViewID: gridItemIndex, isSelectedGridItems: $isSelected)
                    
                        }
                    }.scrollTargetLayout()
                }.scrollTargetBehavior(.viewAligned)
            }
        }
    }
    
    
    
    
        struct GridItemView: View {
        
       // renamed from var id : Int
          let gridItemViewID : Int
    
         @Binding var isSelectedGridItems : [Int]
        
        var body: some View {
            VStack {
                Image(systemName: "globe.europe.africa")
                    }
            .foregroundStyle(isSelectedGridItems.contains(gridItemViewID) ? .red : .black)
            .padding()
            .border(.black)
            //changed from .contextMenu to .overlay
            .overlay(alignment: .bottomTrailing) {
                Menu {
                    Button("Change to Red", action: {
                        if isSelectedGridItems.contains(gridItemViewID) {
                            isSelectedGridItems.removeAll {
                                return $0 == gridItemViewID
                            }
                           
                        }
                        else{
                            isSelectedGridItems.append(gridItemViewID)
                        }
                    })
                   
                } label: {
                    Image(systemName: "ellipsis.circle.fill")
                        .padding(2)
                }.foregroundStyle(.black)
    
            }
        }
    }