Search code examples
iosswiftswiftuimobileview

Multi Selectable and Custom Item Grid SwiftUI


Hello everyone I tried to create a selectable item in SwiftUI like the image bellow, but I don't know how to do that when i touch on each item change the color and color font, and after that add this item to a new state, that would be a list,this is my code and i'm using a library in order to organice the items.

Grid Items

This is my item's code

import SwiftUI

struct SportItem: View {
    
    @EnvironmentObject var modelData: ModelData
    var sport: Sport
    var backgroundColor: Color = Color(red: 103, green: 80, blue: 164)
    
    var body: some View {
        HStack{
            Text(sport.emoji)
            Text("  "+sport.name)
                .bold()
        }.font(.system(size: 14))
        .padding(8)
//        .background(backgroundColor)
        .background(Color(red: 0.403921568627451, green: 0.3137254901960784, blue: 0.6431372549019608))
        .foregroundColor(.white)
        .cornerRadius(15)
        .shadow(color: Color.black, radius: 3)
        
    }
}

struct SportItem_Previews: PreviewProvider {
    
    static var modelData = ModelData()
    
    static var previews: some View {
        SportItem(sport: modelData.sports[6])
            .environmentObject(modelData)
        
    }
}

I try to create a grid, the goal is this file to create the state variable a can choose sport I wanted

import SwiftUI
import WrappingStack

struct SelectSport: View {
    
    var event: Event
    
    @State private var eventForm = Event(id: Int(), name: "", place: "", description: "",date: Date(), beginTime: 1, endTime: 2, img: "", sports: Set<Sport>())
    
    
    
    @State var selectedSports: Set<Sport> =  Set<Sport>()
    
    @EnvironmentObject var modelData: ModelData
    var sportsList: [Sport] { modelData.sports }
    
    
    var body: some View {
        VStack{
            Text("Which sports do you want to include?")
            Text(event.name)
            WrappingHStack {
                ForEach(sportsList){
                    sport in
                    SportItem(sport: sport)
                        .padding(4)
                }
            }
            
        }
        .navigationTitle("Select Sports")
        .navigationBarItems(trailing:
                                NavigationLink(destination:DatePickerEvent(event: Event(id: 1, name: "", place: "", description: "", date: Date(), beginTime: 1, endTime: 1, img: "", sports: Set<Sport>() ))){
                Text("Next")
            }
        )
    }
}

struct SelectSport_Previews: PreviewProvider {
    static var previews: some View {
        SelectSport(event: Event(id: 1, name: "", place: "", description: "",date: Date(), beginTime: 1, endTime: 2, img: "", sports: Set<Sport>()))
            .environmentObject(ModelData())
    }
}

Solution

  • First, add an isSelected property to SportItem, and change how you draw the item depending on isSelected. For example:

    struct SportItem: View {
        
        @EnvironmentObject var modelData: ModelData
        var sport: Sport
        var isSelected: Bool // <— ADD THIS
    
        // CHANGE THIS
        var backgroundColor: Color {
            if isSelected { return Color.red }
            else { return Color(red: 0.403921568627451, green: 0.3137254901960784, blue: 0.6431372549019608) }
        }
    
        var body: some View {
            HStack{
                Text(sport.emoji)
                Text("  "+sport.name)
                    .bold()
            }.font(.system(size: 14))
            .padding(8)
            .background(backgroundColor) // <- CHANGE THIS
            .foregroundColor(.white)
            .cornerRadius(15)
            .shadow(color: Color.black, radius: 3)
            
        }
    }
    

    Then, in SelectSport, make two changes:

    1. Pass the isSelected property to SportItem. The value to pass depends on whether selectedSports contains sport.

    2. Add an .onTapGesture modifier after SportItem that toggles whether selectedSports contains sport.

    For example:

    SportItem(sport: sport, isSelected: selectedSports.contains(sport))
        .onTapGesture {
            selectedSports.formSymmetricDifference([sport])
        }
        .padding(4)