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

视图旋转后拖动触摸位置异常问题求助(含CGAffineTransform代码)

解决旋转后视图拖动偏离的问题

嘿,这个问题我之前踩过坑,核心是你没搞清楚CGAffineTransform的应用顺序和坐标系变换的关系,咱们一步步拆解:

为什么旋转后拖动会偏离?

你的代码里用了 translation.concatenating(rotation),这里有两个关键误区:

  1. 变换顺序搞反了:CGAffineTransform的拼接是「从右往左」应用的——A.concatenating(B) 实际是先执行B变换,再执行A变换。你现在的写法是先旋转视图坐标系,再在旋转后的坐标系上做平移,这就导致平移的x/y轴是跟着视图旋转后的方向走的,而你的触摸位置是基于屏幕(父视图)坐标系的,两者方向不匹配,自然会偏离。
  2. 直接用绝对位置做平移:你每次拖动都直接把touchPosition作为平移参数,但这个位置是屏幕坐标系的绝对坐标,没有考虑视图当前的位置。第二次拖动时,视图已经有了旋转和位移,直接用绝对位置会导致计算完全错误。

正确的修复思路

核心是把「位置平移」和「旋转变换」分开管理,不要混在同一个transform里拼接:

  • 用视图的center属性来管理位置(基于父视图坐标系,不受旋转影响)
  • 单独维护一个旋转角度变量,用transform来设置旋转

具体代码示例

首先定义几个属性记录状态:

// 记录当前的旋转角度(弧度)
private var currentRotation: CGFloat = 0.0
// 触摸开始时的初始位置(父视图坐标系)
private var initialTouchPoint: CGPoint = .zero
// 触摸开始时视图的初始center
private var initialViewCenter: CGPoint = .zero

然后处理触摸事件:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    guard let touch = touches.first else { return }
    // 记录触摸起点和视图当前位置
    initialTouchPoint = touch.location(in: self.view)
    initialViewCenter = targetView.center
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    guard let touch = touches.first else { return }
    let currentTouchPoint = touch.location(in: self.view)
    
    // 计算拖动的偏移量(相对于触摸起点)
    let offsetX = currentTouchPoint.x - initialTouchPoint.x
    let offsetY = currentTouchPoint.y - initialTouchPoint.y
    
    // 更新视图位置(基于父坐标系,不受旋转影响)
    targetView.center = CGPoint(x: initialViewCenter.x + offsetX, y: initialViewCenter.y + offsetY)
    // 保持当前旋转状态
    targetView.transform = CGAffineTransform(rotationAngle: currentRotation)
}

当你需要设置旋转时,只需要更新currentRotation并应用:

// 比如设置45度旋转
currentRotation = CGFloat.pi / 4
targetView.transform = CGAffineTransform(rotationAngle: currentRotation)

额外补充:如果一定要用transform做平移?

如果坚持要通过transform组合平移和旋转,那必须保证平移是在父视图坐标系下进行,正确的变换顺序应该是先平移(父坐标系),再旋转,也就是:

// 注意顺序:先创建旋转变换,再拼接平移
let rotationTransform = CGAffineTransform(rotationAngle: currentRotation)
let translationTransform = CGAffineTransform(translationX: offsetX, y: offsetY)
targetView.transform = rotationTransform.concatenating(translationTransform)

但这种方式需要你始终基于偏移量计算平移,而不是绝对位置,管理起来不如用center直观,所以更推荐前面的方案。

内容的提问来源于stack exchange,提问作者R.Wonder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:29:10