CAShapeLayer缩放异常:二次捏合手势未基于上次缩放后尺寸执行
解决CAShapeLayer二次捏合缩放回到初始尺寸的问题
这个坑我之前踩过!问题出在你每次处理捏合手势时,都是基于图层的初始变换状态来计算缩放,而没有保存上一次缩放后的最终状态。
核心原因
UIPinchGestureRecognizer的scale属性在每次手势开始时都会重置为1.0。如果你的代码每次都直接用gesture.scale去生成新的transform(比如CATransform3DMakeScale(gesture.scale, gesture.scale, 1)),那第二次手势触发时,就会从图层的初始尺寸重新计算,而不是基于第一次缩放后的结果叠加。
解决步骤
- 保存当前缩放状态:添加一个实例变量来记录图层的当前缩放比例,这样每次手势都能基于这个值计算新的缩放。
- 分阶段处理手势状态:在手势开始时记录当前缩放,手势变化时叠加缩放比例,手势结束时更新保存的状态并重置手势的scale。
完整代码示例
import UIKit class ViewController: UIViewController { // 直接持有CAShapeLayer的引用,避免每次取sublayers[0]的风险 private var shapeLayer: CAShapeLayer! // 记录当前的缩放比例 private var currentScale: CGFloat = 1.0 override func viewDidLoad() { super.viewDidLoad() // 初始化你的CAShapeLayer shapeLayer = CAShapeLayer() let path = UIBezierPath(rect: CGRect(x: 50, y: 50, width: 200, height: 200)) shapeLayer.path = path.cgPath shapeLayer.fillColor = UIColor.blue.cgColor view.layer.addSublayer(shapeLayer) // 添加捏合手势 let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchGesture(_:))) view.addGestureRecognizer(pinchGesture) } @objc private func handlePinchGesture(_ gesture: UIPinchGestureRecognizer) { guard let layer = shapeLayer else { return } switch gesture.state { case .began: // 手势开始时,从图层的transform中提取当前缩放比例 // m11是x轴缩放系数,m22是y轴,等比缩放时两者相等 currentScale = layer.transform.m11 case .changed: // 基于当前缩放比例,叠加手势的scale计算新的缩放 let newScale = currentScale * gesture.scale // 更新图层的transform layer.transform = CATransform3DMakeScale(newScale, newScale, 1.0) case .ended, .cancelled: // 手势结束后,更新保存的缩放比例 currentScale = layer.transform.m11 // 重置手势的scale为1.0,避免影响下一次手势 gesture.scale = 1.0 default: break } } }
额外注意事项
- 尽量直接持有CAShapeLayer的引用,不要依赖
sublayers![0]——如果后续添加其他子图层,这个索引可能会失效,还会有数组越界的风险。 - 如果需要支持不等比缩放,需要分别保存x轴和y轴的缩放比例(比如
currentScaleX和currentScaleY),在计算时分别叠加。
内容的提问来源于stack exchange,提问作者ckc
相关产品推荐
相关产品推荐

