Home > Software engineering >  Tapping on a View to also change its sibling views
Tapping on a View to also change its sibling views

Time:12-31

Within a VStack, I have 3 views. A view's selection and colour are toggled when tapping on them. I want the previously selected View to be deselected when selecting the next view.
The tapGesture is implemented in each view. I am not sure what is the best way to achieve this.
Thanks.
Here is the code sample:

struct ContentView: View {
    @State var tile1 = Tile()
    @State var tile2 = Tile()
    @State var tile3 = Tile()
    
    var body: some View {
        VStack {
            TileView(tile: tile1 )
            TileView(tile: tile2 )
            TileView(tile:tile3 )
        }
        .padding()
    }
}

struct Tile: Identifiable, Equatable{
    var id:UUID = UUID()
    var isSelected:Bool = false
}

struct TileView: View {
    
    @State var tile:Tile
    
    var body: some View {
        RoundedRectangle(cornerRadius: 15)
            .fill( tile.isSelected ? Color.red : Color.yellow )
            .frame(height: 100)
            .padding()
            .onTapGesture {
                tile.isSelected.toggle()
            }
    }
}

CodePudding user response:

You need to relate the 3 tiles somehow. An Array is an option. Then once they are related you can change the selection at that level.

extension Array where Element == Tile{
    ///Marks the passed `tile` as selected and deselects other tiles.
    mutating func select(_ tile: Tile) {
        for (idx, t) in self.enumerated(){
            if t.id == tile.id{
                self[idx].isSelected.toggle()
            }else{
                self[idx].isSelected = false
            }
        }
    }
}

Then you can change your views to use the new function.

struct MyTileListView: View {
    @State var tiles: [Tile] = [Tile(), Tile(), Tile()]
    var body: some View {
        VStack {
            ForEach(tiles) { tile in
                TileView(tile: tile, onSelect: {
                    //Use the array to select the tile
                    tiles.select(tile)
                })
            }
        }
        .padding()
    }
}

struct TileView: View {
    //@State just create a copy of the tile `@Binding` is a two-way connection if needed
    let tile:Tile
    ///Called when the tile is selected
    let onSelect: () -> Void
    var body: some View {
        RoundedRectangle(cornerRadius: 15)
            .fill(tile.isSelected ? Color.red : Color.yellow)
            .frame(height: 100)
            .padding()
            .onTapGesture {
                onSelect()
            }
    }
}
  • Related