使用CATransform3DMakeRotation时,Frame与AutoLayout下子视图旋转差异问题
嗨,我来帮你搞定这个旋转变形的问题!
问题根源
当你直接用frame手动布局而不依赖AutoLayout时,很容易忽略视图自身中心与父坐标系的关联。AutoLayout会自动帮你维护视图的center、bounds和frame三者的一致性,但手动设置frame时,若只修改位置和大小,可能会导致视图的旋转锚点(默认是自身中心)偏离预期,最终引发旋转变形。
解决方案
1. 改用center + bounds来手动布局,而非直接设置frame
frame是视图在父坐标系中的“外框”,而center是视图自身中心在父坐标系的位置,bounds是视图自身的大小和内部坐标系。直接设置frame可能会让center和bounds的对应关系出错,旋转时就会变形。正确的手动布局方式应该是:
// 假设父视图MyView的bounds为parentBounds let subViewSize = CGSize(width: 100, height: 100) // 先确定子视图在父视图中的中心位置 let subCenter = CGPoint(x: parentBounds.midX, y: parentBounds.midY) // 设置子视图自身的大小 mySubView.bounds = CGRect(origin: .zero, size: subViewSize) // 设置子视图的中心(锚点默认是自身中心,这样旋转就会绕自己转) mySubView.center = subCenter
2. 确保锚点(anchorPoint)是默认的(0.5, 0.5)
旋转的锚点由视图layer的anchorPoint决定,默认是视图的中心(0.5,0.5)。如果这个值被修改过,旋转就会绕非中心的点进行,导致视觉上的变形。可以在MySubView的初始化方法里明确设置:
override init(frame: CGRect) { super.init(frame: frame) // 确保锚点是自身中心 self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) // 其他初始化代码(比如设置蓝色背景) } required init?(coder: NSCoder) { super.init(coder: coder) self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) }
3. 完善旋转角度的转换逻辑
你给出的rotateAngle属性代码有点截断,这里补全正确的实现,确保角度到弧度的转换准确,并且直接替换transform而非叠加(如果是设置固定角度的话):
var rotateAngle: Int = 0 { didSet { let degreeMeasurement = Measurement(value: Double(rotateAngle), unit: UnitAngle.degrees) let radians = degreeMeasurement.converted(to: .radians).value // 直接设置transform,避免多次旋转叠加导致的偏移 self.transform = CGAffineTransform(rotationAngle: CGFloat(radians)) } }
4. 检查父视图的layoutSubviews方法
如果你的MyView在layoutSubviews里又重新设置了子视图的frame,会覆盖你之前的手动布局设置,导致旋转后变形。如果不用AutoLayout,建议在MyView的layoutSubviews里用center + bounds的方式重新定位子视图:
override func layoutSubviews() { super.layoutSubviews() guard let subView = self.subviews.first as? MySubView else { return } let subSize = CGSize(width: 100, height: 100) subView.bounds = CGRect(origin: .zero, size: subSize) subView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY) }
总结
手动布局时,核心是维护视图center、bounds和锚点的一致性,让旋转的基准点始终是子视图自身的中心,这样就能和AutoLayout下的旋转效果保持一致啦!
内容的提问来源于stack exchange,提问作者Jan

