0

I'm trying to drag the gray square inside the white square manteinig the starting point CGPoint(x: 0, y: 0) and respecting the extreme coordinates, see of the image.

The code works but I have two problems:

  • the mouse doesn't match the center of the gray square when it is dragged;
  • I can't subtract the size of the gray rectangle 30x30 (like red rectangle) from the geometry without changing the extreme value of the coordinate.

I tried to solve both problems with

location.x = max (0, min (geometry.size.width - 15, value.location.x) - 15)
location.y = max (0, min (geometry.size.height - 15 , value.location.y) - 15)

but vertices x and y becomes 0.94 instead 1 and 0.06000000000000005 instead 0.

struct ContentView: View {
    @State private var location: CGPoint = CGPoint(x: 0, y: 0)
    
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            GeometryReader { geometry in
                ZStack(alignment: .topLeading) {
                    Rectangle()
                    .fill(.white)
                    Rectangle()
                    .fill(.gray)
                    .frame(width: 30, height: 30)
                    .offset(x: location.x, y: location.y)
                }
                .gesture(
                    DragGesture(minimumDistance: 0, coordinateSpace: .local)
                    .onChanged { value in
                        location.x = max(0, min(geometry.size.width, value.location.x))
                        location.y = max(0, min(geometry.size.height, value.location.y))

                        print("location",location)
                        print("value location",value.location)

                        let distanceA = distanceA(0, location.x) / geometry.size.width  // < -30
                        let distanceB = distanceB(0, location.y) / geometry.size.height // < -30

                        print(min(distanceA, 1))
                        print(1 - distanceB)
                    }
                )
            }
            .frame(width: 500, height: 500)
        }
    }
    
    func distanceA(_ a: Double, _ b: Double) -> Double {
        let B = b > 0.0 ? b : 0
        let xDist = a - (B)
        return Double(sqrt(pow(xDist, 2)))
    }
    
    func distanceB(_ a: Double, _ b: Double) -> Double {
        let B = b > 0.0 ? b : 0
        let yDist = (B) - a
        return Double(sqrt(pow(yDist, 2)))
    }
}

Wrong geometry drag coordinates

Joannes
  • 2,569
  • 3
  • 17
  • 39

1 Answers1

1

You are attempting to take the small square size into account in the wrong place. The GeometryReader coordinates are the size of the larger square, and that doesn't change. You just need to handle the placement of the smaller square within the larger one. Essentially you need to move it up and left half the size of the square. Commented code:

struct ContentView: View {
    @State private var location: CGPoint = CGPoint(x: 15, y: 15)
    let dragSquareSize: CGFloat = 30
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            GeometryReader { geometry in
                ZStack(alignment: .topLeading) {
                    Rectangle()
                    .fill(.yellow)
                    Rectangle()
                    .fill(.gray)
                    .frame(width: dragSquareSize, height: dragSquareSize)
                    // subtract half of the dimensions of the smaller square to center the squareon the cursor.
                    .offset(x: location.x - dragSquareSize / 2, y: location.y - dragSquareSize / 2)
                    .gesture(
                        DragGesture(minimumDistance: 0, coordinateSpace: .local)
                        .onChanged { value in
                            // Subtract half of the dimensions of the smaller square from geometry here
                            location.x = max(dragSquareSize / 2, min(geometry.size.width - dragSquareSize / 2, value.location.x))
                            location.y = max(dragSquareSize / 2, min(geometry.size.height - dragSquareSize / 2, value.location.y))

                            print("location",location)
                            print("value location",value.location)

                            let distanceA = distanceA(0, location.x) / geometry.size.width  // < -30
                            let distanceB = distanceB(0, location.y) / geometry.size.height // < -30

                            print(min(distanceA, 1))
                            print(1 - distanceB)
                        }
                    )
                }
            }
            .frame(width: 300, height: 300)
        }
    }
    
    func distanceA(_ a: Double, _ b: Double) -> Double {
        let B = b > 0.0 ? b : 0
        let xDist = a - (B)
        return Double(sqrt(pow(xDist, 2)))
    }
    
    func distanceB(_ a: Double, _ b: Double) -> Double {
        let B = b > 0.0 ? b : 0
        let yDist = (B) - a
        return Double(sqrt(pow(yDist, 2)))
    }
}
Yrb
  • 8,103
  • 2
  • 14
  • 44
  • Thanks for the code. In console I noticed that it is not possible to obtain the range `0...1` in vertices but the minimum value is always ` 0.050000000000000044` instead `0` and the maximum value is` 0.95` instead `1`. Is a `GeometryReader` bug? – Joannes Apr 17 '22 at 18:56
  • More likely simple rounding errors. Doubles are essentially approximations. That is why we have Decimals for precise work. – Yrb Apr 17 '22 at 21:20
  • @Yrb , i think 0.05 and 0.95 are mostly taking into account the cube size which is not 0. – Ptit Xav Apr 18 '22 at 10:26
  • That is possible, as the offsets are 1/2 the size, and the offsets come to 0.05 of the frame. – Yrb Apr 18 '22 at 13:23