swiftuidrag-and-drop

Cannot drag again after dropping an element


I have this code:

import SwiftUI

struct ContentView: View {
  @State private var offset = CGSize.zero
  
  var body: some View {
    ZStack {
      Color.red
      HStack {
        Group {
          Image(systemName: "globe")
            .imageScale(.large)
            .foregroundStyle(Color.black)
          
          Image(systemName: "globe")
            .imageScale(.large)
            .foregroundStyle(Color.yellow)
          
          Image(systemName: "globe")
            .imageScale(.large)
            .foregroundStyle(Color.blue)
        }
        .font(.system(size: 40))
        .padding(.horizontal, 10)
        .padding(.vertical, 5)

      }
      .background(
        RoundedRectangle(cornerSize: CGSize(width: 50,height: 50))
          .fill(.white)
      )
      .offset(x: offset.width, y: offset.height)
      .contentShape(Rectangle())
      .gesture(
        DragGesture()
          .onChanged { gesture in
            offset = gesture.translation
          }
      )
      .padding()
    }
    
  }
}

#Preview {
  ContentView()
}

Using this, you drag the stack inside the red rectangle, but after you drop it at the final destination you can drag it again. What am I missing?


Solution

  • I hope I understood the problem.
    The drag gesture's translation property shows how much it moved from current position. So just setting it to offset causes it to snap back to (0, 0) in the start. So we declare an anchor variable to be our anchor point. After end of every drag gesture, we set the anchor to translation with the current value off anchor added together. If we only set it to translation it suffers the same problem. So we add the current anchor value with translation to get our new anchor. And yes its a bit complicated.

    And your .offset() property needs to be after .contentShape() because it will not move the contentShape if you put it after it.

    struct ContentView: View {
        @State private var offset = CGSize.zero
        @State private var anchor = CGSize.zero
        
        var body: some View {
            ZStack {
                Color.red
                HStack {
                    Group {
                        Image(systemName: "globe")
                            .imageScale(.large)
                            .foregroundStyle(Color.black)
                        
                        Image(systemName: "globe")
                            .imageScale(.large)
                            .foregroundStyle(Color.yellow)
                        
                        Image(systemName: "globe")
                            .imageScale(.large)
                            .foregroundStyle(Color.blue)
                    }
                    .font(.system(size: 40))
                    .padding(.horizontal, 10)
                    .padding(.vertical, 5)
                    
                }
                .background(
                    RoundedRectangle(cornerSize: CGSize(width: 50,height: 50))
                        .fill(.white)
                )
                .contentShape(Rectangle())
                .offset(offset)
                .gesture(
                DragGesture()
                    .onChanged { gesture in
                        offset.width = gesture.translation.width + anchor.width
                        offset.height = gesture.translation.height + anchor.height
                    }
                    .onEnded { value in
                        anchor.width += value.translation.width
                        anchor.height += value.translation.height
                    }
                )
                .padding()
            }
            
        }
    }