视图旋转后拖动触摸位置异常问题求助(含CGAffineTransform代码)
解决旋转后视图拖动偏离的问题
嘿,这个问题我之前踩过坑,核心是你没搞清楚CGAffineTransform的应用顺序和坐标系变换的关系,咱们一步步拆解:
为什么旋转后拖动会偏离?
你的代码里用了 translation.concatenating(rotation),这里有两个关键误区:
- 变换顺序搞反了:CGAffineTransform的拼接是「从右往左」应用的——
A.concatenating(B)实际是先执行B变换,再执行A变换。你现在的写法是先旋转视图坐标系,再在旋转后的坐标系上做平移,这就导致平移的x/y轴是跟着视图旋转后的方向走的,而你的触摸位置是基于屏幕(父视图)坐标系的,两者方向不匹配,自然会偏离。 - 直接用绝对位置做平移:你每次拖动都直接把
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
相关产品推荐
相关产品推荐

