Search code examples
swiftswiftuidraggesture

How can I keep my text within the confines of a frame


I have a draggable text and I have a red rectangle . I am trying to have that draggable text within that red rectangle and to only allow it to be dragged inside that rectangle . I have this code so far and it does everything that I need except the restricting the draggable text part to be inside the red rectangle, any suggestions on how I can confine the draggable text to only be inside the red rectangle ?

struct DraginContainerView: View {
    @State var viewState = CGSize.zero

    var body: some View {
        VStack {

            Rectangle()
                .fill(.red)
                .frame(width: 200, height: 200)
            
            
                Text("My Text")
                    .foregroundColor(.black)
                    .offset(x: viewState.width, y: viewState.height)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                viewState = value.translation
                            }.onEnded { value in
                                self.viewState = value.translation
                            }
                    )
        }
    }
}

Solution

  • Change your VStack to a ZStack so the text is on top of your Rectangle()

    We have to get the width and height of the text. This is not easy, but we we can achieve this by applying a GeometryReader to a clear text background.

    The last thing is to check if our translation from the DragGesture is in your predefined width and height of the Rectangle().

    struct DraginContainerView: View {
        @State var viewState = CGVector(dx: 0, dy: 0)
        @State var textProxy = CGSize.zero
        let width: CGFloat = 200
        let height: CGFloat = 200
        
        var body: some View {
            ZStack {
                Rectangle()
                    .fill(.red)
                    .frame(width: width, height: height)
                
                Text("My Text")
                    .foregroundColor(.black)
                    .offset(x: viewState.dx, y: viewState.dy)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                if value.translation.width < (width/2 - textProxy.width/2) && value.translation.width + (width/2 - textProxy.width/2) > 0  {
                                    viewState.dx = value.translation.width
                                }
                                if value.translation.height < (height/2 - textProxy.height/2) && value.translation.height + (height/2 - textProxy.height/2) > 0 {
                                    viewState.dy = value.translation.height
                                }
                            }.onEnded { value in
                                self.viewState = CGVector(dx: 0, dy: 0)
                            }
                    )
                    .background {
                        GeometryReader { proxy in
                            Color.clear
                                .onAppear {
                                    textProxy = proxy.size
                                }
                        }
                    }
            }
        }
    }