You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI带缩放效果的拖拽关闭视图时跳变问题求助

解决SwiftUI拖拽关闭大视图时的缩放跳变问题

问题描述

为大视图添加DragGesture实现拖拽关闭功能时,视图会随手势幅度缩放,但触发关闭后,视图会先跳回原始缩放比例,再通过matchedGeometryEffect动画过渡到小视图,出现明显的跳变卡顿。

问题根源

  1. 原代码在DragGesture的onChanged回调中直接触发关闭逻辑,此时手势仍在进行中,立刻重置gestureOffset = .zero会让scaleEffect瞬间恢复到1,导致跳变。
  2. matchedGeometryEffect的过渡动画没有衔接拖拽时的缩放状态,关闭时大视图直接回到初始尺寸再开始过渡。

修复代码

struct TestView: View {
    @State var showBigView = false
    @Namespace private var animationNameSpace
    @State var gestureOffset: CGSize = .zero
    @State var finalScale: CGFloat = 1.0
    
    var body: some View {
        ZStack {
            if !showBigView {
                Image(systemName: "tshirt")
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(.red)
                    .matchedGeometryEffect(id: "circle", in: animationNameSpace)
                    .frame(width: 100, height: 100)
                    .onTapGesture {
                        withAnimation(.easeInOut) {
                            showBigView = true
                            finalScale = 1.0
                            gestureOffset = .zero
                        }
                    }
            } else {
                let currentScale = max(1 - (gestureOffset.height / 600), 0.6)
                Image(systemName: "tshirt")
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(.red)
                    .matchedGeometryEffect(id: "circle", in: animationNameSpace)
                    .frame(width: 300, height: 300)
                    .scaleEffect(currentScale)
                    .offset(y: gestureOffset.height)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                guard value.translation.height > 0 else { return }
                                gestureOffset = value.translation
                            }
                            .onEnded { value in
                                if value.translation.height > 150 {
                                    finalScale = currentScale
                                    withAnimation(.easeInOut(duration: 0.3)) {
                                        showBigView = false
                                        gestureOffset = .zero
                                    }
                                } else {
                                    withAnimation(.easeInOut) {
                                        gestureOffset = .zero
                                    }
                                }
                            }
                    )
                    .transition(.scale(scale: finalScale))
            }
        }
    }
}

关键修改说明

  • 新增finalScale变量:记录关闭瞬间的缩放比例,让过渡动画从当前缩放状态启动,避免跳变。
  • 关闭逻辑移至onEnded:确保手势完全结束后再判断是否关闭,避免中途状态突变。
  • 添加垂直偏移:让视图随拖拽手势下移,操作更符合直觉。
  • 限制最小缩放:设置currentScale最小值为0.6,防止视图过度缩小影响体验。
  • 自定义过渡动画:用finalScale作为过渡的初始缩放值,让matchedGeometryEffect的动画和拖拽缩放无缝衔接。

内容的提问来源于stack exchange,提问作者Noah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:10:19