Why does MagnificationGesture only update the state once in SwiftUI?

Viewed 1035

I wish to have the following view respond to the magnification gestures, but it only works the first time. The view can be magnified as expected, but after the view is released, subsequent touches do not trigger the gesture.

Is there a way to have the gesture work continuously?

struct Card: View {
    @State var magScale: CGFloat = 1

    var magnification: some Gesture {
        MagnificationGesture().onChanged { gesture in magScale = gesture }
    }

    var body: some View {
        Rectangle()
            .frame(width: 200, height: 200, alignment: .center)
            .scaleEffect(self.magScale)
            .gesture(magnification)
    }
}

The view is used like so:

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.blue
            Card()
        }
    }
}
2 Answers

Here is a solution. Tested with Xcode 12 (for below version some syntax might needed adapting)

demo

struct Card: View {
    @State var magScale: CGFloat = 1
    @State var progressingScale: CGFloat = 1

    var magnification: some Gesture {
        MagnificationGesture()
            .onChanged { progressingScale = $0 }
            .onEnded {
                magScale *= $0
                progressingScale = 1
            }
    }

    var body: some View {
        Rectangle()
            .frame(width: 200, height: 200, alignment: .center)
            .scaleEffect(self.magScale * progressingScale)
            .gesture(magnification)
    }
}

Below code is working fine on the device. No major change except adding the definition of tap and drag.

struct ContentView: View {
    var body: some View {

        ZStack {
            Color.blue
            Card()
        }
    }
}

struct Card: View {
    @State var magScale: CGFloat = 1
    @State var tapped: Bool = false
    @State var isDragging = false

    var magnification: some Gesture {
        MagnificationGesture().onChanged { gesture in
            self.magScale = gesture
        }
    }

    var tap: some Gesture {
        TapGesture(count: 1).onEnded { _ in
        self.tapped = !self.tapped
        }
    }

    var drag: some Gesture {
        DragGesture()
            .onChanged {_ in
                self.isDragging = true
            }
            .onEnded {_ in
                self.isDragging = false
            }
    }


    var body: some View {
        Rectangle()
            .foregroundColor(Color.green)
            .frame(width: 200, height: 200, alignment: .center)
            .scaleEffect(self.magScale)             
          .gesture(tap.simultaneously(with:magnification).simultaneously(with: drag))
    }
}
Related