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

为何ZStack未执行旋转与缩放,但其内部内容却响应手势?

问题:ZStack应用旋转/缩放效果无效,仅内部Image响应手势

我在ZStack上添加了旋转手势(rotation gesture)和放大手势(magnify gesture),明明将rotationEffect与scaleEffect应用于该ZStack,但ZStack本身未发生旋转和缩放,而其中的Image却能响应手势进行旋转与缩放。

ScreenShoot

原代码

var body: some View {
    
    NavigationView {
        VStack {
            GeometryReader { geo in
                let w = geo.size.width
                let h = geo.size.height
                let size = getSize(h, w)
                
                if size.width > 0 && size.height > 0 {
                    
                    ZStack {
                        Image(uiImage: service.backgroundImage!)
                            .resizable()
                            .scaledToFill()
                            .frame(width: size.width, height: size.height, alignment: .center)
                            .clipped()

                        // 目标ZStack
                        ZStack{
                            Image(uiImage: foregroundImage)
                                .resizable()
                                .scaledToFit()
                                .background(GeometryGetter(rect: $imageRect))
                        }
                        .rotationEffect(rotationAngle)
                        .scaleEffect(scale)
                        .gesture(DragGesture(minimumDistance: 0 , coordinateSpace: .local)
                                    .onChanged { value in
                            self.offset.x += value.location.x - value.startLocation.x
                            self.offset.y += value.location.y - value.startLocation.y
                            print("->> Location - \(value.location)")
                        })
                        .offset(x: offset.x, y: offset.y)
                        .gesture(simultaneous)
                        .background(.blue)
                    }
                    .frame(width: size.width, height: size.height, alignment: .center)
                    .clipped()
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(AppDefaults.Theme.secondaryColor)
                }
            }
            bottomContainerView()
        }
        .background(Color.black.ignoresSafeArea())
    }
}

问题原因

  • 内层ZStack没有明确的尺寸约束,其大小完全由内部Image决定,导致旋转/缩放效果看起来仅作用于Image
  • 手势叠加顺序不合理,且未统一坐标空间,可能导致手势识别目标偏离ZStack容器

修复方案

给内层ZStack添加明确尺寸约束,调整手势合并方式,确保变换效果作用于整个容器:

var body: some View {
    NavigationView {
        VStack {
            GeometryReader { geo in
                let w = geo.size.width
                let h = geo.size.height
                let size = getSize(h, w)
                
                if size.width > 0 && size.height > 0 {
                    
                    ZStack {
                        Image(uiImage: service.backgroundImage!)
                            .resizable()
                            .scaledToFill()
                            .frame(width: size.width, height: size.height, alignment: .center)
                            .clipped()

                        // 给内层ZStack设置明确尺寸,匹配背景图空间
                        ZStack{
                            Image(uiImage: foregroundImage)
                                .resizable()
                                .scaledToFit()
                                .background(GeometryGetter(rect: $imageRect))
                        }
                        .frame(width: size.width, height: size.height)
                        .rotationEffect(rotationAngle)
                        .scaleEffect(scale)
                        .offset(x: offset.x, y: offset.y)
                        // 合并拖拽与旋转/缩放手势,支持同时触发
                        .gesture(
                            DragGesture(minimumDistance: 0, coordinateSpace: .local)
                                .onChanged { value in
                                    self.offset.x += value.location.x - value.startLocation.x
                                    self.offset.y += value.location.y - value.startLocation.y
                                    print("->> Location - \(value.location)")
                                }
                                .simultaneously(with: simultaneous)
                        )
                        .background(.blue)
                    }
                    .frame(width: size.width, height: size.height, alignment: .center)
                    .clipped()
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(AppDefaults.Theme.secondaryColor)
                }
            }
            bottomContainerView()
        }
        .background(Color.black.ignoresSafeArea())
    }
}

关键修改说明

  1. 给内层ZStack添加.frame(width: size.width, height: size.height),让它占据完整的背景图空间,此时旋转/缩放会作用于整个蓝色背景的ZStack容器
  2. 使用.simultaneously(with:)合并拖拽手势与旋转/缩放手势,避免手势冲突,确保多个操作可以同时响应
  3. 调整offset的位置到变换效果之后,保证偏移是基于旋转/缩放后的坐标系,避免位置混乱

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 13:10:27