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

SwiftUI中如何同时实现旋转、平移与缩放手势?

解决SwiftUI中签名视图的平移、缩放与旋转手势冲突问题

修复后的完整代码

struct SignatureView: View {
    @State private var location: CGPoint = CGPoint(x: 150, y: 300)
    @State private var width: CGFloat = 100
    @State private var height: CGFloat = 100
    
    @State private var totalRotation: Double = 0.0
    @GestureState private var tempRotation: Double = 0.0
    
    private var currentRotation: Double {
        totalRotation + tempRotation
    }
    
    var body: some View {
        VStack {
            GeometryReader { geometry in
                ZStack {
                    // 可交互的签名容器
                    ZStack(alignment: .bottomTrailing) {
                        Rectangle()
                            .stroke(style: StrokeStyle(lineWidth: 1, dash: [5]))
                            .fill(.blue)
                            .frame(width: width, height: height)
                        
                        // 黑色控制旋钮(同时处理缩放和旋转)
                        Circle()
                            .fill(.black)
                            .frame(width: 25, height: 25)
                            .gesture(knobGesture)
                            .zIndex(1)
                    }
                    .rotationEffect(Angle(degrees: currentRotation))
                    .position(location)
                    .gesture(panGesture)
                }
                .frame(width: geometry.size.width, height: geometry.size.height)
            }
        }
        .background(Color.black.opacity(0.3))
        .overlay(
            VStack {
                Spacer()
                Button("Save") {}
            }
        )
        .ignoresSafeArea(.all)
    }
    
    // 旋钮的缩放+旋转手势
    private var knobGesture: some Gesture {
        DragGesture(minimumDistance: 0)
            .updating($tempRotation) { value, state, transaction in
                // 将拖拽点转换到未旋转的容器本地坐标系,抵消当前旋转的影响
                let containerCenter = CGPoint(x: width/2, y: height/2)
                let rotationTransform = CGAffineTransform(rotationAngle: -CGFloat(currentRotation * .pi / 180))
                let translatedTransform = rotationTransform.translatedBy(x: -containerCenter.x, y: -containerCenter.y)
                
                let startPoint = value.startLocation.applying(translatedTransform)
                let currentPoint = value.location.applying(translatedTransform)
                
                // 计算旋转角度变化
                let startAngle = atan2(startPoint.y, startPoint.x)
                let currentAngle = atan2(currentPoint.y, currentPoint.x)
                state = Double((currentAngle - startAngle) * 180 / .pi)
                
                // 计算缩放比例,保证尺寸等比例变化
                let startDistance = hypot(startPoint.x, startPoint.y)
                let currentDistance = hypot(currentPoint.x, currentPoint.y)
                let scale = currentDistance / startDistance
                width = max(50, width * scale)
                height = width
            }
            .onEnded { _ in
                totalRotation += tempRotation
            }
    }
    
    // 容器的平移手势
    private var panGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                location.x += value.translation.width
                location.y += value.translation.height
            }
    }
    
    func loadImageFromDocumentDirectory(filename: String) -> UIImage? {
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let fileURL = documentsDirectory.appendingPathComponent(filename)
        
        do {
            let imageData = try Data(contentsOf: fileURL)
            return UIImage(data: imageData)
        } catch {
            print("Error loading image: \(error)")
            return nil
        }
    }
}

关键修改说明

  • 合并旋钮手势逻辑:把缩放和旋转整合到同一个DragGesture中,彻底解决手势竞争问题。拖拽黑色旋钮时,可同时通过拖拽的径向距离调整尺寸,通过角度变化调整旋转角度。
  • 修正坐标系计算:计算旋转角度前,将拖拽点转换到未旋转的容器本地坐标系,抵消当前旋转状态对角度计算的干扰,确保旋转逻辑在任何角度下都准确。
  • 简化状态管理:用GestureState临时存储旋转增量,避免重复计算;移除冗余的fingerLocation、startLocation等状态,让代码更简洁。
  • 优化手势优先级:平移手势绑定到容器视图,旋钮手势绑定到黑色圆形,SwiftUI会自动处理手势传递,保证容器平移和旋钮操作互不干扰。
  • 统一缩放逻辑:通过拖拽点到容器中心的距离比例计算缩放,比原代码的translation.width更直观准确,确保尺寸始终等比例变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:10:14