为什么CGAffineTransform旋转效果在UIBezierPath与UIImageView上表现不同
问题根源
- 变换应用顺序相反:
UIBezierPath.apply(_:)按调用顺序正向应用变换,而UIView.transform的链式调用是矩阵右乘,实际执行顺序与书写顺序完全相反,直接复用路径的变换顺序必然出现错位。 - 旋转角度两侧不匹配:两套代码中相同
Direction枚举值对应的旋转角度数值不一致,是视觉偏移的核心原因之一。 - 默认锚点不匹配:UIView默认旋转锚点是自身中心(坐标(0.5, 0.5)),而你的路径变换逻辑是绕矩形顶部中点旋转,锚点不匹配导致旋转后平移出现大半径圆周偏移。
修复方案
提供两种可直接落地的修复方案,均能实现与绘制路径完全对齐的效果:
方案1:修改锚点(逻辑更直观)
直接将UIImageView的旋转锚点调整为路径的旋转基准点(矩形顶部中点),不需要调整变换顺序:
func addImageView(_ x: CGFloat, _ y: CGFloat, _ direction: Direction) { let myImageView = MyImageView(frame: CGRect(x: 0, y: 0, width: 116, height: 200)) // 修正原代码笔误 myImageView.backgroundColor = .systemBlue // 调整锚点为矩形顶部中点,和路径变换的旋转基准完全对齐 myImageView.layer.anchorPoint = CGPoint(x: 0.5, y: 0) // 锚点修改后直接将位置对齐目标坐标 myImageView.center = CGPoint(x: x, y: y) let pi = CGFloat.pi var angle: CGFloat = 0 // 完全对齐DraggingView中的角度映射规则 switch direction { case .downLeft: angle = pi / 4.0 case .downRight: angle = -pi / 4.0 case .left: angle = pi / 2.0 case .right: angle = -pi / 2.0 case .up: angle = pi case .upLeft: angle = 3 * pi / 4.0 case .upRight: angle = -3 * pi / 4.0 default: angle = 0 } // 仅需应用旋转即可 myImageView.transform = CGAffineTransform(rotationAngle: angle) view.addSubview(myImageView) }
方案2:调整变换顺序(不修改锚点)
如果不想改动锚点,反转变换的书写顺序即可适配UIView的变换规则:
func addImageView(_ x: CGFloat, _ y: CGFloat, _ direction: Direction) { let myImageView = MyImageView(frame: CGRect(x: 0, y: 0, width: 116, height: 200)) myImageView.backgroundColor = .systemBlue var viewTransform = CGAffineTransform.identity // 第一步:平移到目标点(对应路径的最后一步变换,写在最前) viewTransform = viewTransform.translatedBy(x: x, y: y) let pi = CGFloat.pi var angle: CGFloat = 0 // 对齐角度映射规则 switch direction { case .downLeft: angle = pi / 4.0 case .downRight: angle = -pi / 4.0 case .left: angle = pi / 2.0 case .right: angle = -pi / 2.0 case .up: angle = pi case .upLeft: angle = 3 * pi / 4.0 case .upRight: angle = -3 * pi / 4.0 default: angle = 0 } // 第二步:应用旋转 viewTransform = viewTransform.rotated(by: angle) // 第三步:左移宽度一半(对应路径的第一步变换,写在最后) viewTransform = viewTransform.translatedBy(x: -58, y: 0) myImageView.transform = viewTransform view.addSubview(myImageView) }
内容的提问来源于stack exchange,提问作者Dru Freeman
相关产品推荐
相关产品推荐

