Search code examples
swiftswiftuigesturedrag

SwiftUI Drag gesture applies to parent view


I have applied a drag gesture with some action to the Image views inside Foreach view, but when I drag an image - it visually drags all the images within the parent view and even in the other Foreach view with images. The action applied to the selected image works properly though. How can I make only selected image move and all others stay still?

Gesture variables:

@State var position = CGSize.zero
@GestureState private var dragOffset = CGSize.zero

There is the code:

ScrollView(.horizontal) {
                HStack(alignment: .center, spacing: 0) {
                    ForEach ((0..<player.playersCards.count), id: \.self) {number in
                        VStack {
                            Image(player.playersCards[number].pic)
                                .resizable()
                                .modifier(CardStyle())
                                .offset(dragOffset)
                                .gesture(
                                    DragGesture()
                                        .updating($dragOffset, body: { (value, state, transaction) in

                                            state = value.translation
                                        })
                                        .onEnded({ (value) in
                                            self.position.height += value.translation.height
                                            self.position.width += value.translation.width

                                            //player's turn
                                            
                                        })
                                )
                                .animation(.spring())

                            Text("\(ai.getPower(card: player.playersCards[number]))")
                                .modifier(TextModifier())
                        }
                    }
                }
            }

Solution

  • It’s because all of your images in forEach are set to single @GestureState property, so when one is dragged all mapped to it gets dragged on body refresh. You can have separate array that store each drag state of your card at particular index uniquely.

    struct LanguageView: View {
    
    
        @State var position = CGSize.zero
        @GestureState private var dragOffset:[CGSize]
    
        init() {
            let  dragOffsets = [CGSize](repeating: CGSize.zero, count: 5) // Initialise with your model array count
            _dragOffset = GestureState(wrappedValue: dragOffsets)
        }
    
        var body: some View {
            ScrollView(.horizontal) {
                HStack(alignment: .center, spacing: 5) {
                    ForEach ((0..<5)) {number in
                        VStack {
                            Image("ABC")
                                .resizable()
                                .frame(width: 300, height: 100)
                                //.modifier(CardStyle())
                                .offset(dragOffset[number])
                                .gesture(
                                    DragGesture()
                                        .updating($dragOffset, body: { (new, existing, transaction) in
    
                                            existing[number] = new.translation
                                        })
                                        .onEnded({ (value) in
                                            self.position.height += value.translation.height
                                            self.position.width += value.translation.width
    
                                            //player's turn
    
                                        })
                                )
                                .animation(.spring())
    
                            Text("Foo")
                            // .modifier(TextModifier())
                        }
                    }
                }
            }.frame(width: 500, height: 100)
        }
    }