SwiftUI - drawing a temporary placeholder

I have a base view which displays a number of subviews at fixed positions. Its for laying out tables in a restaurant. The base view is an image of the restaurant floor, the subviews are images of tables. The user can click on a table and drag it about - so far so good, all working.

Tables should only be placed in certain places, for arguments sake we'll say every 100 pixels (x and y) in the base view.

While the user drags the table about smoothly, I'd like a "shadow" rectangle to snap to just the nearest available place that's allowable like it's saying "this is where the table will snap to when you drop it"

Can someone help this old dinosuar by explaining how to include a temporary rectangle in a SwiftUI view that updates it's position during the DragGesture of the table? Thank you!

Replies

I did something similar as follows (credit to https://sarunw.com/posts/move-view-around-with-drag-gesture-in-swiftui/):

  • create 2 state var to keep track of original position and current finger position
    @GestureState private var fingerLocation: CGPoint? = nil
    @GestureState private var startLocation: CGPoint? = nil 

create a state var for destination rect

@State var newTablePos = CGRect.zero
  • create 2 dragGesture (see explanation in the reference link)
    var simpleDrag: some Gesture {
        DragGesture()
            .onChanged { value in
                
                var newLocation = startLocation ?? location
                newLocation.x += value.translation.width
                newLocation.y += value.translation.height
                if newLocation.x < 200 { newLocation.x = 200 } // if you don't want to move too far
                if newLocation.x > parentViewSize.width - 100 { newLocation.x = parentViewSize.width - 100 } // if you don't want to move too far
                if newLocation.y < 200 { newLocation.y = 200 } 
                if newLocation.y > parentViewSize.height - 100 { newLocation.y = parentViewSize.height - 100 } 
                self.location = newLocation
                // Use newLocation to compute what is the expected destination
                // update newTablePos if it changes
            }
            .updating($startLocation) { (value, startLocation, transaction) in
                startLocation = startLocation ?? location
            }
    }
    
    var fingerDrag: some Gesture {
        DragGesture()
            .updating($fingerLocation) { (value, fingerLocation, transaction) in
                fingerLocation = value.location
            }
    }
  • In the body of the view, you call on the rect you move:
        .gesture(simpleDrag.simultaneously(with: fingerDrag))
        if let fingerLocation = fingerLocation {
            // draw destination rect here, using newTablePos

Hope that helps

Thanks for the reply!I got this working, and don;t need to subclass Gesture, by using an ofset as a State variable and updating the offset in .onChanged and updating the position in .ended