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

SwiftUI中旋转视图后拖拽手势偏移量与位置的调整方法

解决旋转视图后拖拽手势偏移异常的问题

这个问题很典型——当视图旋转后,拖拽手势返回的translation是基于父视图的坐标系的,而你的图片已经有了旋转角度,直接用原始的平移值就会导致拖拽方向和你预期的不一致(比如视图转了90度,你想往右拖,结果图片可能往上走)。

解决思路是:把拖拽得到的平移向量,根据当前视图的总旋转角度做坐标逆变换,将父坐标系下的平移值转换成图片自身坐标系下的偏移量,这样拖拽方向就会和图片的朝向匹配了。

下面是修改后的完整代码:

import SwiftUI

struct ImageView: View {
    @State private var dragOffset: CGSize = .zero
    @State private var position: CGSize = .zero
    @State private var currentRotation: Angle = .zero
    @GestureState private var twistAngle: Angle = .zero
    
    public var body: some View {
        let totalRotation = currentRotation + twistAngle
        
        let rotationGesture = RotationGesture(minimumAngleDelta: .degrees(10))
            .updating($twistAngle, body: { (value, state, _) in
                state = value
            })
            .onEnded{
                self.currentRotation += $0
            }
        
        let dragGesture = DragGesture()
            .onChanged({ (value) in
                // 将父坐标系的平移转换为旋转后视图坐标系的偏移
                let radians = totalRotation.radians
                let transformedX = value.translation.width * cos(radians) + value.translation.height * sin(radians)
                let transformedY = -value.translation.width * sin(radians) + value.translation.height * cos(radians)
                
                self.dragOffset = CGSize(width: transformedX, height: transformedY)
            })
            .onEnded({ (value) in
                let radians = totalRotation.radians
                let transformedX = value.translation.width * cos(radians) + value.translation.height * sin(radians)
                let transformedY = -value.translation.width * sin(radians) + value.translation.height * cos(radians)
                
                self.position.width += transformedX
                self.position.height += transformedY
                self.dragOffset = .zero
            })
        
        let gestures = rotationGesture
            .simultaneously(with: dragGesture)
        
        Image.placeholder320x192
            .offset(x: dragOffset.width + position.width, y: dragOffset.height + position.height)
            .rotationEffect(totalRotation)
            .gesture(gestures, including: .gesture)
    }
}

// 保留原占位符结构
extension Image {
    static var placeholder320x192: Image {
        Image(systemName: "photo")
            .resizable()
            .frame(width: 320, height: 192)
            .background(Color.gray)
    }
}

关键逻辑解释:

  • 总旋转角度计算:totalRotation = currentRotation + twistAngle,包含了已确认的旋转角度和手势中的临时旋转角度,确保变换用的是实时的旋转状态。
  • 坐标变换公式:
    我们用逆旋转矩阵来转换平移向量——因为视图旋转了θ角,要让拖拽方向和视图自身的上下左右一致,就需要把父坐标系的平移值“反向旋转”θ角,对应公式:
    transformedX = 原始X * cosθ + 原始Y * sinθ
    transformedY = -原始X * sinθ + 原始Y * cosθ
    
    这里的θ是总旋转角度的弧度值,cos和sin函数需要弧度参数,所以用totalRotation.radians转换。

这样修改后,不管图片旋转到什么角度,拖拽操作都会和你直观的预期一致,比如你朝着图片的“右侧”拖拽,图片就会往那个方向移动,而不是按照父视图的坐标系偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 17:37:34