SwiftUI I want to use simultaneousGesture but also isolated gesture

Viewed 244

I want to use RotationGesture and MagnificationGesture simultaneously. However I also use these gestures individually. Here is my code.


struct TmpView10: View {
    @State private var magnify: CGFloat = 1
    @State private var lastMagnify: CGFloat = 1
    @State private var angle: Angle = .degrees(.zero)
    @State private var lastAngle: Angle = .degrees(.zero)
    
    var body: some View {
        VStack () {
            ZStack () {
                Image("someImage")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(self.magnify)
                    .rotationEffect(self.angle)
                    .gesture(RotationGesture()
                        .onChanged{ angle in
                            self.angle = angle + self.lastAngle
                        }
                        .onEnded { angle in
                            self.lastAngle = self.angle
                        }
                    )
                    .simultaneousGesture(MagnificationGesture(minimumScaleDelta: 0.5)
                        .onChanged{ v in
                            let delta = v / self.lastMagnify
                            self.lastMagnify = v
                            self.magnify *= delta
                        }
                        .onEnded{ v in
                            self.lastMagnify = 1
                        }
                    )
                    .clipShape(Circle())
                    .overlay(
                        Circle().stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .red, .purple]), startPoint: .bottomLeading, endPoint: .topTrailing), lineWidth: 5))
            }
        }
    }
}

By this code I can use two gestures simultaneously. But once I only rotate Image, MagnificationGesture stops working. Are there any way to use both simultaneousGesture and individual gesture ?

2 Answers

Coding around use of minimumScaleDelta by using an .updating GestureState seems to solve the problem.

struct ContentView: View {
@State private var lastMagnify: CGFloat = 1
@State private var lastAngle: Angle = .degrees(.zero)
@GestureState private var magnification: (active: Bool, factor: CGFloat) = (active: false, CGFloat(1))
@GestureState private var angle: Angle = .degrees(.zero)

var rotateAndMagnifyGesture: some Gesture {
    let rotate = RotationGesture()
        .updating($angle) { (newAngle, gestureState, _) in
            gestureState = newAngle
        }
        .onEnded { angle in
            lastAngle += angle
        }
    let magnify = MagnificationGesture()
        .updating($magnification) { (value, gestureState, _) in
            gestureState.factor = value
            
            let delta = value / lastMagnify
            if abs(1 - delta) > 0.3 {
                gestureState.active = true
            }
        }
        .onEnded { value in
            lastMagnify *= value
        }
    return rotate.simultaneously(with: magnify)
}

var body: some View {
    VStack () {
        ZStack () {
            Image(systemName: "diamond")
                .resizable()
                .rotationEffect((lastAngle + angle))
                .scaleEffect(magnification.active ? (magnification.factor * lastMagnify) : lastMagnify)
                .aspectRatio(contentMode: .fit)
                .gesture(rotateAndMagnifyGesture)
                .clipShape(Circle())
                .overlay(
                    Circle().stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .red, .purple]), startPoint: .bottomLeading, endPoint: .topTrailing), lineWidth: 5))
        }
    }
}

}

I think SwiftUI doesn't like the combined gesture having the "minimumScaleDelta" in the "magnify" portion of the gesture. If you remove "minimumScaleDelta", then the gesture seems to work, but this could just be because it's nearly impossible with fingers to just execute a rotation gesture in isolation without detecting some form of a pinch / zoom.

Related