Dismiss gesture with scale effect

216 views Asked by At

When I add a drag gesture to dismiss my "big view" it scales to the amount of the gesture but if it dismisses, the view jumps to its origin scale and animates the view to the "small view".

Here is my example Code: ` struct TestView: View { @State var showBigView = false

@Namespace private var animationNameSpace

@State var gestureOffset: CGSize = .zero

var body: some View {
    ZStack {
        if !showBigView {
            Image(systemName: "tshirt")
                .resizable()
                .scaledToFit()
                .foregroundColor(.red)
                .matchedGeometryEffect(id: "circel", in: animationNameSpace)
                .transition(.scale(scale: 1))
                .frame(width: 100, height: 100)
                .onTapGesture {
                    withAnimation {
                        showBigView = true
                    }
                }
        } else {
            Image(systemName: "tshirt")
                .resizable()
                .scaledToFit()
                .foregroundColor(.red)
                .matchedGeometryEffect(id: "circel", in: animationNameSpace)
                .transition(.scale(scale: 1))
                .frame(width: 300, height: 300)
                .scaleEffect(abs(gestureOffset.height / 1000 - 1))
                .gesture(DragGesture().onChanged {
                    guard $0.translation.height > 0 else { return }
                    self.gestureOffset = $0.translation

                    if abs($0.translation.height) > 150 {
                        withAnimation {
                            showBigView = false
                            gestureOffset = .zero
                        }
                    }
                })
        }
    }
}

} `

It should not jump when the view dismisses with a specific scaleEffect.

1

There are 1 answers

1
John Sauer On

I believe you're trying to:

  • Show the tee shirt image in a 100 by 100 frame.
  • When tapped, it grows to a 300 by 300 frame with animation.
  • Dragging downward shrinks the image. When the drag height exceeds 150 points, the image shrinks to its original 100 by 100 frame with animation.

This approach works:

struct TestView: View {
    @State private var isBig = false
    @State private var frame = TestView.smallFrame
    @State private var scale = 1.0
    static private let smallFrame = 100.0
    static private let bigFrame = 300.0
    
    var body: some View {
        Image(systemName: "tshirt")
            .resizable()
            .scaledToFit()
            .frame(width: frame, height: frame)
            .scaleEffect(scale)
            .foregroundColor(.red)
            .onTapGesture {
                if !isBig {
                    withAnimation {
                        frame = TestView.bigFrame
                    }
                    isBig = true
                }
            }
            .gesture(DragGesture().onChanged {
                let dragHeight = $0.translation.height
                if isBig && dragHeight > 0 {
                    if dragHeight >= 150 {
                        frame = TestView.bigFrame * scale
                        scale = 1
                        withAnimation {
                            frame = TestView.smallFrame
                        }
                        isBig = false
                    } else {
                        scale = 1 - (dragHeight / 1000)
                    }
                }
            }.onEnded { _ in
                withAnimation {
                    scale = 1
                }
            })
    }
}

It's not necessary to create two separate images and synchronize them with matchedGeometryEffect. Instead, you can use your @State boolean to control whether or not gestures have any effect.