2

I want to be able to drag an arrow shape around on top of an image in swiftUI. I have tried to simplify it and limit it to a rectangle here in the beginning. I have taken some inspiration from this post: Limit drag to circular bounds in SwiftUI

And this is my take on it ATM. I hope someone can point me in the right direction :)

So imagine that the gray rectangle is the image and the blue one is the arrow, then I only want to be able to drag the blue rectangle inside the gray rectangle.

struct ImageView: View {
    @State private var position = CGPoint(x: 100, y: 100)
    private var rectSize: CGFloat = 600
    var body: some View {
        VStack {
            Text("Current postion = (x: \(Int(position.x)), y: \(Int(position.y))")
            
            Rectangle()
                .fill(.gray)
                .frame(width: rectSize, height: rectSize)
                .overlay(
                    Rectangle()
                        .fill(.blue)
                        .frame(width: rectSize / 4, height: rectSize / 4)
                        .position(x: position.x, y: position.y)
                        .gesture(
                            DragGesture()
                                .onChanged({ value in
                                    let currentLocation = value.location
                                    let center = CGPoint(x: self.rectSize/2, y: self.rectSize/2)
                                    
                                    let distance = center.distance(to: currentLocation)
                                    if distance > self.rectSize / 2 {
                                        let const = (self.rectSize / 2) / distance
                                        let newLocX = (currentLocation.x - center.x) * const+center.x
                                        let newLocY = (currentLocation.y - center.y) * const+center.y
                                        self.position = CGPoint(x: newLocX, y: newLocY)
                                    } else {
                                        self.position = value.location
                                    }
                                })
                                )
                )
        }
    }
}
  • Does this answer your question https://stackoverflow.com/a/63693584/12299030? – Asperi Jun 29 '22 at 08:56
  • 1
    Almost, I have tried it out, but didn't manage to convert it to my problem, unfortunately... Maybe because im not sure if I understand it 100% :I – Frederik Hjorth Jun 29 '22 at 12:30

1 Answers1

2

You're almost there. As you're outer shape is a rect, you don't need to calculate complicated distances. It's enough to check whether the position is still in the outer rect, and limit drag to its size values (0...rectSize). If you don't want your selection frame to move over the borders, you have to correct by 1/2 of its size (rectSize / 4 / 2)

struct ContentView: View {
    
    @State private var position = CGPoint(x: 100, y: 100)
    private var rectSize: CGFloat = 350
    
    var body: some View {
        VStack {
            Text("Current postion = (x: \(Int(position.x)), y: \(Int(position.y))")
            
            Rectangle()
                .fill(.gray)
                .frame(width: rectSize, height: rectSize)
                .overlay(
                    Rectangle()
                        .fill(.clear)
                        .border(.blue, width: 2.0)
                        .contentShape(Rectangle())
                        .frame(width: rectSize / 4, height: rectSize / 4)
                        .position(position)
                        .gesture(
                            DragGesture()
                                .onChanged { value in
                                    // limit movement to min and max value 
                                    let limitedX = max(min(value.location.x, rectSize - rectSize / 8), rectSize / 8)
                                    let limitedY = max(min(value.location.y, rectSize - rectSize / 8), rectSize / 8)

                                    self.position = CGPoint(x: limitedX,
                                                            y: limitedY)
                                }
                        )
                )
        }
    }
}
ChrisR
  • 9,523
  • 1
  • 8
  • 26