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

SwiftUI图片拖拽缩放实现及Sheet拖拽时图片仅垂直缩放问题排查

SwiftUI图片拖拽缩放实现及Sheet拖拽时图片仅垂直缩放问题排查

嘿,我明白你现在遇到的困扰——拖拽Sheet向下滑动时,图片虽然能垂直方向变大甚至超出边界,但水平方向却没跟着按比例缩放,看起来像是被“压扁”或者只拉伸了高度对吧?咱们来拆解下问题所在,再给出可行的修复方案。

问题根源分析

先看你现有代码里的图片处理逻辑:

AsyncImage(
    url: URL(string: "https://images.unsplash.com/photo-1551709645-3f16f608bb80?ixlib"),
    content: { image in
        image.resizable()
            .frame(height: geometryImage.size.height * 0.2 + sheetPosition)
            .aspectRatio(contentMode: .fit)
    },
    placeholder: {}
)
.frame(height: geometryImage.size.height * 0.2 + sheetPosition)

这里的核心问题有两个:

  • 你给AsyncImage和内部图片都设置了固定高度,再加上.aspectRatio(contentMode: .fit)的限制——这个模式会让图片保持原有宽高比,但宽度会被父容器的宽度(也就是屏幕宽度)卡死。所以当你增加高度时,图片宽度最多只能到屏幕宽度,无法继续按比例扩展,看起来就只有垂直方向在变化。
  • sheetPosition直接取Sheet的minY值,把它直接加到高度上的逻辑太生硬,没有和图片的宽高比例关联起来,导致缩放逻辑不连贯。

修复方案:基于缩放比例的整体缩放

我们换个思路,通过计算Sheet的拖拽偏移量得到整体缩放因子,用scaleEffect让图片按比例整体缩放,同时允许它超出父容器边界。修改后的完整代码如下:

struct SizePreferenceKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue: Value = 0
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}

struct ContentView: View {
    @State private var selectedPresentationDetent: PresentationDetent = .fraction(0.6)
    @State private var sheetMinY: CGFloat = 0
    // 记录Sheet初始位置,用于计算缩放比例
    @State private var initialSheetMinY: CGFloat = 0
    
    var body: some View {
        GeometryReader { screenGeometry in
            VStack {
                VStack(alignment: .center) {
                    Spacer()
                    Text("Hello, world!")
                        .padding()
                    AsyncImage(
                        url: URL(string: "https://images.unsplash.com/photo-1551709645-3f16f608bb80?ixlib"),
                        content: { image in
                            image.resizable()
                                .aspectRatio(contentMode: .fit)
                                // 根据Sheet偏移计算的缩放因子,实现整体缩放
                                .scaleEffect(scaleFactor)
                                // 允许图片超出父容器,不被裁剪
                                .allowsHitTesting(false)
                        },
                        placeholder: {}
                    )
                    // 给图片设置初始基础尺寸
                    .frame(height: screenGeometry.size.height * 0.2)
                    Spacer()
                }
                .frame(height: screenGeometry.size.height * 0.5 + screenGeometry.safeAreaInsets.top)
            }
        }
        .sheet(isPresented: .constant(true)) {
            GeometryReader { sheetGeometry in
                Color.blue
                    .ignoresSafeArea()
                    .interactiveDismissDisabled()
                    .presentationBackgroundInteraction(.enabled)
                    .presentationDetents([
                        .fraction(0.6),
                        .fraction(0.3)
                    ], selection: $selectedPresentationDetent)
                    .presentationDragIndicator(.visible)
                    .preference(key: SizePreferenceKey.self, value: sheetGeometry.frame(in: .global).minY)
                    .onAppear {
                        // 记录Sheet初始的minY值,作为缩放计算的基准
                        initialSheetMinY = sheetGeometry.frame(in: .global).minY
                    }
            }
            .onPreferenceChange(SizePreferenceKey.self) { newValue in
                sheetMinY = newValue
            }
        }
    }
    
    // 计算平滑的缩放比例:Sheet下拉越远,图片缩放越大
    private var scaleFactor: CGFloat {
        let maxScale: CGFloat = 2.0  // 最大缩放倍数
        let minScale: CGFloat = 1.0  // 初始缩放倍数
        // 计算拖拽偏移的比例,映射到缩放范围
        let offsetRatio = (initialSheetMinY - sheetMinY) / initialSheetMinY
        return minScale + (maxScale - minScale) * offsetRatio
    }
}

关键修改点说明

  • 改用scaleEffect实现整体缩放:让图片同时按宽高比例放大,从根源解决只拉伸高度的问题。
  • 平滑的缩放因子计算:通过Sheet初始位置和当前位置的差值,计算出合理的缩放比例,保证拖拽时图片缩放自然流畅。
  • 移除固定高度限制:不再给图片设置动态高度,改用初始高度配合缩放因子,让图片自由扩展。
  • 允许图片超出边界:不设置裁剪逻辑,同时添加.allowsHitTesting(false)避免图片遮挡Sheet的拖拽操作。

这样调整后,拖拽Sheet向下滑动时,图片就会整体按比例放大,同时垂直方向超出边界,完全符合你的需求啦!

备注:内容来源于stack exchange,提问作者ammerzon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 11:10:29