Limit rectangle to screen edge on drag gesture

2.1k views Asked by At

I'm just getting started with SwiftUI and I was hoping for the best way to tackle the issue of keeping this rectangle in the bounds of a screen during a drag gesture. Right now it goes off the edge until it reaches the middle of the square (I think cause I'm using CGPoint).

I tried doing some math to limit the rectangle and it succeeds on the left side only but it seems like an awful way to go about this and doesn't account for varying screen sizes. Can anyone help?

struct ContentView: View {
    @State private var pogPosition = CGPoint()
    
    var body: some View {
        PogSound()
            .position(pogPosition)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        self.pogPosition = value.location
                        
                        // Poor solve
                        if(self.pogPosition.x < 36) {
                            self.pogPosition.x = 36
                        }
                }
                .onEnded { value in
                    print(value.location)
                }
        )
    }
}

enter image description here

1

There are 1 answers

1
Asperi On BEST ANSWER

Here is a demo of possible approach (for any view, cause view frame is read dynamically).

Demo & tested with Xcode 12 / iOS 14

enter image description here

struct ViewSizeKey: PreferenceKey {
    static var defaultValue = CGSize.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}

struct ContentView: View {
    @State private var pogPosition = CGPoint()
    @State private var size = CGSize.zero

    var body: some View {
        GeometryReader { gp in
            PogSound()
                .background(GeometryReader {
                    Color.clear
                        .preference(key: ViewSizeKey.self, value: $0.frame(in: .local).size)
                })
                .onPreferenceChange(ViewSizeKey.self) {
                    self.size = $0
                }
                .position(pogPosition)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            let rect = gp.frame(in: .local)
                                .inset(by: UIEdgeInsets(top: size.height / 2.0, left: size.width / 2.0, bottom: size.height / 2.0, right: size.width / 2.0))
                            if rect.contains(value.location) {
                                self.pogPosition = value.location
                            }
                        }
                        .onEnded { value in
                            print(value.location)
                        }
            )
            .onAppear {
                let rect = gp.frame(in: .local)
                self.pogPosition = CGPoint(x: rect.midX, y: rect.midY)
            }
        }.edgesIgnoringSafeArea(.all)
    }
}