简体   繁体   中英

SwiftUI Drag gesture is not ending

I was trying to create a custom View with rotation effects in SwiftUI . Instead of using rotation gestures, I was trying to use the Drag Gesture to rotate the Parent by adding a separate view to the parent. Dragging the child (The Blue Dot) would rotate its parent (ZStack). But I've faced difficulty while dragging the blue dot. The drag gesture is not ending. Can you guys help me to find out what I did wrong?

import SwiftUI

struct EditingHolder: View {

    /* For Rotation Circle Drag */
    @State private var currDragRect : CGSize = .zero
    @State private var prevDragRect : CGSize = .zero
    var rectRadius : CGFloat {
        let sum : CGFloat = 2*pow(150, 2)
        return sqrt(sum/4)
    }

    var dragRadius : CGFloat {
        let height = prevDragRect.height + currDragRect.height
        let width = prevDragRect.width + currDragRect.width
        let sum = pow(height, 2) + pow(width, 2)
        return sqrt(sum/4)
    }

    var rotateAngle : CGFloat{
        let angle = asin(dragRadius/rectRadius)
        print("🍄 Angle Produced = ", angle)
        return angle
    }

    var body: some View {
        /* **** Gestures **** */
        let rotateDrag = DragGesture()
            .onChanged({ value in
                print("🏵 Rotate Circle Drag Started ...")
                currDragRect = value.translation

            }).onEnded({ _ in
                print("🏵 Rotate Circle Drag Ended ✅")
                prevDragRect.height += currDragRect.height
                prevDragRect.width += currDragRect.width
                currDragRect = .zero
            })

        //************* Views *******************

        GeometryReader { geo in
            ZStack(alignment: .center) {
                Rectangle()
                    .padding()
                    .foregroundColor(Color.yellow)

                ///Rotate Circle `top`
                Circle()
                    .foregroundColor(Color.blue)
                    .frame(width: 20, height: 20)
                    .position(x: 150 - 3, y: 3)
                    .gesture(rotateDrag)

            }.frame(width:150, height: 150, alignment: .center)
                .border(.green, width: 3)
                .position(x: geo.size.width/2, y: geo.size.height/2)
                .rotationEffect(.radians(rotateAngle))

        }

        //************* Views *******************
    }
}

struct EditingHolder_Previews: PreviewProvider {
    static var previews: some View {
        EditingHolder()
    }
}

To provide high priority to a specific gesture there is a modifier called .highPriorityGesture() . Have a look here for a better explanation How to use gestures in SwiftUI .

I've updated the angle calculations,

    /* **** Gestures **** */
    let rotateDrag = DragGesture()
        .onChanged({ value in
            print("🏵 Rotate Circle Drag Started ...")
            
            let difY = center.y - value.location.y
            let difX = center.x - value.location.x
            
            //Initial Angle when the drag started..
            if deltaAngle == 0{
                deltaAngle = atan2(difY, difX)
            }else{
                angle = atan2(difY, difX) - deltaAngle
            }

        }).onEnded({ _ in
            print("🏵 Rotate Circle Drag Ended ✅")
            withAnimation {
                angle = 0
                deltaAngle = 0
            }
        })

    //************* Views *******************

Now to add .highPriorityGesture(rotateDrag) to ZStack .

.onTapGesture() is added to get the center for angle calculation. Tap on the view and then rotate by dragging the blue dot. Here is the final implementation,

    struct EditingHolder: View {

    /* For Rotation Circle Drag */
    @State private var center : CGPoint = .zero
    @State private var angle : CGFloat = 0
    @State private var deltaAngle : CGFloat = 0

    var body: some View {
        /* **** Gestures **** */
        let rotateDrag = DragGesture()
            .onChanged({ value in
                print("🏵 Rotate Circle Drag Started ...")
                
                let difY = center.y - value.location.y
                let difX = center.x - value.location.x
                
                //Initial Angle when the drag started..
                if deltaAngle == 0{
                    deltaAngle = atan2(difY, difX)
                }else{
                    angle = atan2(difY, difX) - deltaAngle
                }

            }).onEnded({ _ in
                print("🏵 Rotate Circle Drag Ended ✅")
                withAnimation {
                    angle = 0
                    deltaAngle = 0
                }
            })

        //************* Views *******************

        GeometryReader { geo in
            ZStack(alignment: .center) {
                Rectangle()
                    .padding()
                    .foregroundColor(Color.yellow)

                ///Rotate Circle `top`
                Circle()
                    .foregroundColor(Color.blue)
                    .frame(width: 20, height: 20)
                    .position(x: 150 - 3, y: 3)
                    .gesture(rotateDrag)

            }.frame(width:150, height: 150, alignment: .center)
                .border(.green, width: 3)
                .position(x: geo.size.width/2, y: geo.size.height/2)
                .rotationEffect(Angle(radians: angle))
            /* You have make the gesture a high priority */
                .highPriorityGesture(rotateDrag)
                .onTapGesture {
                    print("☘️ Center assigned..")
                    center = CGPoint(x: geo.frame(in: .global).size.width/2, y: geo.frame(in: .global).size.height/2)
                    
                }

        }

        //************* Views *******************
    }
}

struct EditingHolder_Previews: PreviewProvider {
    static var previews: some View {
        EditingHolder()
    }
}

The sequence of drag gesture and rotation is important, otherwise SwiftUI looses context of the dragged view (which is changing by drag).

Also you don't need GeometryReader. Here is an example that works in regards to the dragging, the angle calculation needs some more work.

struct ContentView: View {
    
    /* For Rotation Circle Drag */
    @State private var currDragRect : CGSize = .zero
    @State private var prevDragRect : CGSize = .zero
    let rectRadius : CGFloat = 75
    
    var dragRadius : CGFloat {
        let height = prevDragRect.height + currDragRect.height
        let width = prevDragRect.width + currDragRect.width
        let sum = pow(height, 2) + pow(width, 2)
        return sqrt(sum/4)
    }
    
    var rotateAngle : CGFloat{
        let x = min(1, max(-1, dragRadius/rectRadius)) // asin only defined in -1...1
        let angle = asin(x)
        print("🍄 Angle Produced = ", angle)
        return angle
    }
    
    var body: some View {
        
        /* **** Gestures **** */
        let rotateDrag = DragGesture()
            .onChanged { value in
                print("🏵 Rotate Circle Drag Started ...")
                currDragRect = value.translation
                
            }
            .onEnded { _ in
                print("🏵 Rotate Circle Drag Ended ✅")
                prevDragRect.height += currDragRect.height
                prevDragRect.width += currDragRect.width
                currDragRect = .zero
            }
        
        //************* Views *******************
        
        ZStack(alignment: .topTrailing) {
            Rectangle()
                .padding()
                .foregroundColor(Color.yellow)
                .border(.green, width: 3)
            
            ///Rotate Circle `top`
            Circle()
                .foregroundColor(Color.blue)
                .frame(width: 20, height: 20)
                .offset(x: 8, y: -8)
            
        }
        .rotationEffect(.radians(rotateAngle))  // rotation here
        .gesture(rotateDrag)                    // drag here
        .frame(width:150, height: 150, alignment: .center)
        
        //************* Views *******************
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM