使用CABasicAnimation缩放CAShapeLayer会导致它转换



我有一个CAShapeLayer,它被画成一个圆圈作为给定的point:

let circlePath = UIBezierPath(arcCenter: point, radius: 20.0, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

我希望能够使用CABasicAnimation缩放这个图层。我尝试了几种方法,但大多数给我同样的问题:圆圈适当地缩放,但似乎也翻译/移动在同一时间。圆的缩放和平移速率似乎受到point值的影响:point越接近原点,圆的运动速度越慢。我显然遗漏了一些基本的东西,但我一直没能找到它。下面是我迄今为止尝试过的最简单的构建动画的方法:

let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0

shapeLayer.add(growthAnimation, forKey: nil)
self.view.layer.addSublayer(shapeLayer)

我试过设置"转换"(而不是"transform.scale"),我试过使用一个NSValue作为动画的toValue,我试过添加转换来尝试保持它在当前位置,但没有运气。

我目前的理论是,这个层实际上比圆更大,所以当整个层缩放时,圆看起来只是在平移。但是我不能给图层的边框或背景颜色上色,我不确定我该如何证明它。

"我目前的理论是,图层实际上比圆圈大,这样整个图层就会缩放…">

你现在的理论是正确的。

试着这样写:

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

let radius: CGFloat = 20.0

let point = CGPoint(x: radius, y: radius)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0

shapeLayer.add(growthAnimation, forKey: nil)
// set the layer frame
shapeLayer.frame = CGRect(x: view.frame.midX - radius, y: view.frame.midY - radius, width: radius * 2.0, height: radius * 2.0)

shapeLayer.backgroundColor = UIColor.green.cgColor
self.view.layer.addSublayer(shapeLayer)

}

编辑-这可能使事情更清楚…

如果你运行这个例子,它会创建4个圆形图层:

  • 在没有层框架的0,0处圆中心
  • 在没有给定层帧的情况下,圆心在100,100
  • 层帧x: 100, y: 240, w: 40, h: 400,0圆心(40为半径* 2)…圆心位于框架的左上角
  • 圆中心在radius,radius(so, 20,20),层帧x: 100, y: 380, w: 40, h: 40(40为半径* 2)…圆圈的中心现在位于图层帧
  • 的中心

当你点击视图上的任何地方,所有4层将执行相同的缩放动画(减慢到3秒,使其更容易观看)。

应该然后清楚路径和层框架如何影响转换。

class ViewController: UIViewController {
func test1() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()

// no frame set for the shape layer
// point is at top-left corner of shape layer frame
//  since we haven't set a frame for the layer, it's top-left corner of the view
let point = CGPoint(x: 0, y: 0)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

self.view.layer.addSublayer(shapeLayer)

shapeLayer.backgroundColor = UIColor.green.cgColor

}
func test2() -> Void {

let radius: CGFloat = 20.0

let shapeLayer = CAShapeLayer()

// no frame set for the shape layer

// set point to 100,100
let point = CGPoint(x: 100, y: 100)

let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

self.view.layer.addSublayer(shapeLayer)

shapeLayer.backgroundColor = UIColor.green.cgColor

}
func test3() -> Void {

let radius: CGFloat = 20.0

let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,240
shapeLayer.frame = CGRect(x: 100.0, y: 240.0, width: radius * 2.0, height: radius * 2.0)
// point is at top-left corner of shape layer frame
let point = CGPoint(x: 0, y: 0)

let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.orange.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

self.view.layer.addSublayer(shapeLayer)

shapeLayer.backgroundColor = UIColor.green.cgColor

}

func test4() -> Void {

let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,380
shapeLayer.frame = CGRect(x: 100.0, y: 380.0, width: radius * 2.0, height: radius * 2.0)
// set point to center of layer frame
let point = CGPoint(x: radius, y: radius)

let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0

self.view.layer.addSublayer(shapeLayer)

shapeLayer.backgroundColor = UIColor.green.cgColor


}

override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap))
view.addGestureRecognizer(t)
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

test1()
test2()
test3()
test4()

}

@objc func didTap() -> Void {
scaleLayers()
}

func scaleLayers() -> Void {

let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 3.0
guard let layers = view.layer.sublayers else {
return
}
layers.forEach { layer in
if let shapeLayer = layer as? CAShapeLayer {
shapeLayer.add(growthAnimation, forKey: nil)
}
}
}
}

最新更新