在动画期间无法正确定位子层
Trouble positioning a sublayer correctly during animation
问题是我应该如何定义和设置我的形状图层的位置以及应该如何更新它以便图层出现在我期望它在动画期间出现的位置?也就是说,形状应该粘在棍子的末端。
我有一个名为 containerLayer
的 CALayer 实例,它有一个子层,它是一个名为 shape
的 CAShapeLayer 实例。 containerLayer
应该将 shape
放置在特定位置 unitLoc
,如下所示:
class ContainerLayer: CALayer, CALayerDelegate {
// ...
override func layoutSublayers() {
super.layoutSublayers()
if !self.didSetup {
self.setup()
self.didSetup = true
}
updateFigure()
setNeedsDisplay()
}
func updateFigure() {
figureCenter = self.bounds.center
figureDiameter = min(self.bounds.width, self.bounds.height)
figureRadius = figureDiameter/2
shapeDiameter = round(figureDiameter / 5)
shapeRadius = shapeDiameter/2
locRadius = figureRadius - shapeRadius
angle = -halfPi
unitLoc = CGPoint(x: self.figureCenter.x + cos(angle) * locRadius, y: self.figureCenter.y + sin(angle) * locRadius)
shape.bounds = CGRect(x: 0, y: 0, width: shapeDiameter, height: shapeDiameter)
shape.position = unitLoc
shape.updatePath()
}
// ...
}
我无法找到正确的方法来指定此位置之前和更改 containerLayer.bounds
的调整大小动画期间的位置。我确实知道我遇到的问题是我没有以动画将按照我期望的方式显示它的方式设置位置。
我已经尝试使用 CABasicAnimation(keyPath: "position")
为位置设置动画,它比我之前尝试的效果有所改善,但它仍然关闭。
@objc func resize(sender: Any) {
// MARK:- animate containerLayer bounds & shape position
// capture bounds value before changing
let oldBounds = self.containerLayer.bounds
// capture shape position value before changing
let oldPos = self.containerLayer.shape.position
// update the constraints to change the bounds
isLarge.toggle()
updateConstraints()
self.view.layoutIfNeeded()
let newBounds = self.containerLayer.bounds
let newPos = self.containerLayer.unitLoc
// set up the bounds animation and add it to containerLayer
let baContainerBounds = CABasicAnimation(keyPath: "bounds")
baContainerBounds.fromValue = oldBounds
baContainerBounds.toValue = newBounds
containerLayer.add(baContainerBounds, forKey: "bounds")
// set up the position animation and add it to shape layer
let baShapePosition = CABasicAnimation(keyPath: "position")
baShapePosition.fromValue = oldPos
baShapePosition.toValue = newPos
containerLayer.shape.add(baShapePosition, forKey: "position")
containerLayer.setNeedsLayout()
self.view.layoutIfNeeded()
}
我也试过像这样用presentation layer来设置位置,貌似也能关闭,但还是关闭了。
class ViewController: UIViewController {
//...
override func loadView() {
super.loadView()
displayLink = CADisplayLink(target: self, selector: #selector(animationDidUpdate))
displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.default)
//...
}
@objc func animationDidUpdate(displayLink: CADisplayLink) {
let newCenter = self.containerLayer.presentation()!.bounds.center
let new = CGPoint(x: newCenter.x + cos(containerLayer.angle) * containerLayer.locRadius, y: newCenter.y + sin(containerLayer.angle) * containerLayer.locRadius)
containerLayer.shape.position = new
}
//...
}
class ContainerLayer: CALayer, CALayerDelegate {
// ...
func updateFigure() {
//...
//shape.position = unitLoc
//...
}
// ...
}
稍微夸张一下,我能够更清楚地说明您的代码中发生了什么。在我的示例中,圆形图层应该保持背景视图高度的 1/3:
动画开始时,背景视图已在动画的结束 处设置为其最终大小。你看不到,因为动画依赖于描绘层的表示层,它是不变的;但观点本身已经改变。因此,当您定位形状图层的形状时,并且根据视图进行定位时,您是在调整大小并将其定位在动画 将 需要的位置 结束。因此它跳到它的最终大小和位置,只有当我们到达动画结束时才有意义。
好的,但现在考虑一下:
这样不是更好吗?它是如何完成的?好吧,使用我已经掌握的原则 ,我得到了一个带有自定义动画的层 属性。结果是在动画的每一帧上,我都会得到一个事件(该层的 draw(in:)
方法)。我通过重新计算形状层的路径来对此做出回应。因此,我在 动画的每一帧 上为形状层提供了一条新路径,因此它表现得很流畅。它停留在正确的位置,它根据背景视图的大小平滑地调整大小,并且它的笔触粗细始终保持不变。
问题是我应该如何定义和设置我的形状图层的位置以及应该如何更新它以便图层出现在我期望它在动画期间出现的位置?也就是说,形状应该粘在棍子的末端。
我有一个名为 containerLayer
的 CALayer 实例,它有一个子层,它是一个名为 shape
的 CAShapeLayer 实例。 containerLayer
应该将 shape
放置在特定位置 unitLoc
,如下所示:
class ContainerLayer: CALayer, CALayerDelegate {
// ...
override func layoutSublayers() {
super.layoutSublayers()
if !self.didSetup {
self.setup()
self.didSetup = true
}
updateFigure()
setNeedsDisplay()
}
func updateFigure() {
figureCenter = self.bounds.center
figureDiameter = min(self.bounds.width, self.bounds.height)
figureRadius = figureDiameter/2
shapeDiameter = round(figureDiameter / 5)
shapeRadius = shapeDiameter/2
locRadius = figureRadius - shapeRadius
angle = -halfPi
unitLoc = CGPoint(x: self.figureCenter.x + cos(angle) * locRadius, y: self.figureCenter.y + sin(angle) * locRadius)
shape.bounds = CGRect(x: 0, y: 0, width: shapeDiameter, height: shapeDiameter)
shape.position = unitLoc
shape.updatePath()
}
// ...
}
我无法找到正确的方法来指定此位置之前和更改 containerLayer.bounds
的调整大小动画期间的位置。我确实知道我遇到的问题是我没有以动画将按照我期望的方式显示它的方式设置位置。
我已经尝试使用 CABasicAnimation(keyPath: "position")
为位置设置动画,它比我之前尝试的效果有所改善,但它仍然关闭。
@objc func resize(sender: Any) {
// MARK:- animate containerLayer bounds & shape position
// capture bounds value before changing
let oldBounds = self.containerLayer.bounds
// capture shape position value before changing
let oldPos = self.containerLayer.shape.position
// update the constraints to change the bounds
isLarge.toggle()
updateConstraints()
self.view.layoutIfNeeded()
let newBounds = self.containerLayer.bounds
let newPos = self.containerLayer.unitLoc
// set up the bounds animation and add it to containerLayer
let baContainerBounds = CABasicAnimation(keyPath: "bounds")
baContainerBounds.fromValue = oldBounds
baContainerBounds.toValue = newBounds
containerLayer.add(baContainerBounds, forKey: "bounds")
// set up the position animation and add it to shape layer
let baShapePosition = CABasicAnimation(keyPath: "position")
baShapePosition.fromValue = oldPos
baShapePosition.toValue = newPos
containerLayer.shape.add(baShapePosition, forKey: "position")
containerLayer.setNeedsLayout()
self.view.layoutIfNeeded()
}
我也试过像这样用presentation layer来设置位置,貌似也能关闭,但还是关闭了。
class ViewController: UIViewController {
//...
override func loadView() {
super.loadView()
displayLink = CADisplayLink(target: self, selector: #selector(animationDidUpdate))
displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.default)
//...
}
@objc func animationDidUpdate(displayLink: CADisplayLink) {
let newCenter = self.containerLayer.presentation()!.bounds.center
let new = CGPoint(x: newCenter.x + cos(containerLayer.angle) * containerLayer.locRadius, y: newCenter.y + sin(containerLayer.angle) * containerLayer.locRadius)
containerLayer.shape.position = new
}
//...
}
class ContainerLayer: CALayer, CALayerDelegate {
// ...
func updateFigure() {
//...
//shape.position = unitLoc
//...
}
// ...
}
稍微夸张一下,我能够更清楚地说明您的代码中发生了什么。在我的示例中,圆形图层应该保持背景视图高度的 1/3:
动画开始时,背景视图已在动画的结束 处设置为其最终大小。你看不到,因为动画依赖于描绘层的表示层,它是不变的;但观点本身已经改变。因此,当您定位形状图层的形状时,并且根据视图进行定位时,您是在调整大小并将其定位在动画 将 需要的位置 结束。因此它跳到它的最终大小和位置,只有当我们到达动画结束时才有意义。
好的,但现在考虑一下:
这样不是更好吗?它是如何完成的?好吧,使用我已经掌握的原则 draw(in:)
方法)。我通过重新计算形状层的路径来对此做出回应。因此,我在 动画的每一帧 上为形状层提供了一条新路径,因此它表现得很流畅。它停留在正确的位置,它根据背景视图的大小平滑地调整大小,并且它的笔触粗细始终保持不变。