如何在移动旋转后的UIViewB时,保持UIViewC在UIViewA中的原位置?
解决UIView变换后子视图回归原坐标系位置的问题
在iOS开发中,视图层级为UIViewA → UIViewB → UIViewC,记录UIViewC在UIViewA坐标系的初始位置后,对UIViewB执行旋转和平移操作,需要让UIViewC回到UIViewA中的原位置,但直接调整UIViewC的Y位置会因UIViewB的transform导致结果异常。
核心问题是UIView应用transform后,其内部坐标系会发生旋转、缩放等变换,直接修改子视图的frame会基于变换后的坐标系计算,导致位置偏差。正确的做法是利用UIKit的坐标转换方法,自动处理变换带来的坐标系变化。
具体实现步骤
- 保留已记录的UIViewC在UIViewA中的初始位置
initialCPositionInA - 将该初始位置从UIViewA的坐标系转换到当前状态(已旋转平移)的UIViewB坐标系中
- 设置UIViewC的origin为转换后的坐标
修改后的完整代码
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Create UIViewA let viewA = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500)) viewA.backgroundColor = .red self.view.addSubview(viewA) // Create UIViewB let viewB = UIView(frame: CGRect(x: 150, y: 150, width: 200, height: 200)) viewB.backgroundColor = .green viewA.addSubview(viewB) // Create UIViewC let viewC = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100)) viewC.backgroundColor = .blue viewB.addSubview(viewC) // Initial position of viewC in viewA's coordinate system let initialCPositionInA = viewC.convert(viewC.bounds.origin, to: viewA) // Rotate viewB by 45 degrees viewB.transform = CGAffineTransform(rotationAngle: .pi / 4) // 可替换为任意旋转角度 // Calculate the movement delta of viewB let movementDelta = CGPoint(x: 0, y: -50) // Move viewB up by -50 pixels viewB.center = CGPoint(x: viewB.center.x, y: viewB.center.y + movementDelta.y) // --- 核心修正代码 --- // 将ViewA中的初始位置转换到当前ViewB的坐标系中 let targetPositionInB = viewB.convert(initialCPositionInA, from: viewA) // 设置ViewC的origin为目标位置 viewC.frame.origin = targetPositionInB } }
原理说明
UIView的convert(_:from:)方法会自动处理以下因素:
- 视图层级的父子关系
- 父视图(ViewB)的所有transform(旋转、缩放、平移)
- 视图的bounds和frame变化
因此无论ViewB设置了何种旋转角度或平移距离,转换后的坐标都能准确对应到ViewB当前坐标系中的位置,确保ViewC回到UIViewA中的初始位置。
内容的提问来源于stack exchange,提问作者WCK
相关产品推荐
相关产品推荐

