将视图添加到视图层次结构后如何触发动画
How to trigger an animation after adding the view to the view hierarchy
我正在尝试为正在绘制的圆制作动画,我知道该怎么做,但是,我似乎无法找到有关如何在单击按钮后触发动画的任何信息或者在服务更新数据之后。
最简单的解决方案是使用 CAShapeLayers。我需要更新时间,然后以几个圆圈的形式显示出来。
我也有能够在 drawRect 方法中执行此操作的代码,但我还不知道哪个是更新圆圈的更好解决方案。你们知道怎么做吗?
这是我在使用 CAShapeLayers 时的代码:
private var _greyCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeOneCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeTwoCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeThreeCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeFourCircleLayer: CAShapeLayer = CAShapeLayer()
private var _solidOrangeCircleLayer: CAShapeLayer = CAShapeLayer()
private var _currentDisplayedTime: Double = Double(0.0)
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
private func configure() {
addCAShapeLayer(self._greyCircleLayer, colour: StyleGlobals.StrokeColor.CGColor)
addCAShapeLayer(self._transpartentOrangeOneCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeTwoCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeThreeCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeFourCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._solidOrangeCircleLayer, colour: StyleGlobals.SolidOrange.CGColor)
}
然后我尝试使用以下方法为图层设置动画,但它不起作用。它仅在将图层添加到视图层次结构后调用它时才有效:
func animateCircle(duration: NSTimeInterval, shapeLayer: CAShapeLayer, timeStart: CGFloat, timeEnd: CGFloat) {
/*//let duration = 1.0
let delay = 0.0 // delay will be 0.0 seconds (e.g. nothing)
let options = UIViewAnimationOptions.CurveEaseInOut // change the timing curve to `ease-in ease-out`
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
// any changes entered in this block will be animated
shapeLayer.strokeEnd = timeEnd
}, completion: { finished in
// any code entered here will be applied
// once the animation has completed
})*/
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
// Set the animation duration appropriately
animation.duration = duration
// Animate from 0 (no circle) to 1 (full circle)
animation.fromValue = timeStart
animation.toValue = timeEnd
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// Set the circleLayer's strokeEnd property to 1.0 now so that it's the
// right value when the animation ends.
shapeLayer.strokeEnd = timeEnd
// Do the actual animation
shapeLayer.addAnimation(animation, forKey: "strokeEnd")
}
通常会在您添加时添加 UIView
:
self.addSubView(viewToAdd);
如果您在 UIView
中插入了动画代码(不管它是哪种 UIControl
元素),您可以在 drawRect()
中触发绘图这个:
viewToAdd.setNeedsDisplay();
setNeedsDisplay
触发视图更新自身并调用永远不应直接调用的 drawRect
方法。
您也可以让视图在准备就绪时自行绘制。您可以在 UIView
中添加方法 didMoveToSuperView()
,然后调用 setNeedsDisplay()
.
这是您要找的吗?
class MyView: UIView {
override func didMoveToSuperview() {
println("Animate Me!")
}
}
所以我终于在下面的教程中找到了答案:
http://blog.pixelingene.com/2012/02/animating-pie-slices-using-a-custom-calayer/
查看 PieSliceLayer class,它显示了如何使用可观察的字段为更改设置动画。
此外,在将图层添加到 UIView class 时,您需要对其调用 setNeedsDisplay() 才能显示它。默认不显示。
下面是我的代码:
class HourLayer: CAShapeLayer {
@NSManaged private var _startAngle: CGFloat
@NSManaged private var _endAngle: CGFloat
@NSManaged private var _fillColor: UIColor
@NSManaged private var _strokeWidth: CGFloat
@NSManaged private var _strokeColor: UIColor
@NSManaged private var _duration: CFTimeInterval
private var _previousEndAngle: CGFloat = 0.0
private var _previousStartAngle: CGFloat = 0.0
private var _nextLayerToAnimate: HourLayer?
private var _nextLayerToAnimateStartAngle: CGFloat?
private var _nextLayerToAnimateEndAngle: CGFloat?
private var _nextLayerToAnimateDuration: CFTimeInterval?
private let _lenghtOfArc: CGFloat = CGFloat(2 * M_PI)
internal var StartAngle: CGFloat {
get {
return _startAngle
}
set {
_startAngle = newValue
}
}
internal var EndAngle: CGFloat {
get {
return _endAngle
}
set {
_endAngle = newValue
}
}
internal var FillColor: UIColor {
get {
return _fillColor
}
set {
_fillColor = newValue
}
}
internal var StrokeWidth: CGFloat {
get {
return _strokeWidth
}
set {
_strokeWidth = newValue
}
}
internal var StrokeColor: UIColor {
get {
return _strokeColor
}
set {
_strokeColor = newValue
}
}
internal var DurationOfAnimation: CFTimeInterval {
get {
return _duration
}
set {
_duration = newValue
}
}
required override init!() {
super.init()
self._startAngle = 0.0
self._endAngle = 0.0
self._fillColor = UIColor.clearColor()
self._strokeWidth = 4.0
self._strokeColor = UIColor.blackColor()
self._duration = 1.0
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("required init(:coder) is missing")
}
override init!(layer: AnyObject!) {
super.init(layer: layer)
if layer.isKindOfClass(HourLayer) {
var other: HourLayer = layer as HourLayer
self._startAngle = other._startAngle
self._endAngle = other._endAngle
self._fillColor = other._fillColor
self._strokeWidth = other._strokeWidth
self._strokeColor = other._strokeColor
self._duration = other._duration
}
}
override var frame: CGRect {
didSet {
self.setNeedsLayout()
self.setNeedsDisplay()
}
}
override func actionForKey(event: String!) -> CAAction! {
if event == "_startAngle" || event == "_endAngle" {
return makeAnimationForKey(event)
}
return super.actionForKey(event)
}
/*internal func prepareLayerForAnimationAfterThisLayer(layer: HourLayer, startAngle: CGFloat, endAngle: CGFloat, duration: CFTimeInterval) {
self._nextLayerToAnimate = layer
self._nextLayerToAnimateStartAngle = startAngle
self._nextLayerToAnimateEndAngle = endAngle
self._nextLayerToAnimateDuration = duration
}*/
private func makeAnimationForKey(event: String) -> CABasicAnimation {
// We want to animate the strokeEnd property of the circleLayer
let animation: CABasicAnimation = CABasicAnimation(keyPath: event)
// Set the animation duration appropriately
animation.duration = self._duration
// When no animation has been triggered and the presentation layer does not exist yet.
if self.presentationLayer() == nil {
if event == "_startAngle" {
animation.fromValue = self._previousStartAngle
self._previousStartAngle = self._startAngle
} else if event == "_endAngle" {
animation.fromValue = self._previousEndAngle
self._previousEndAngle = self._endAngle
}
} else {
animation.fromValue = self.presentationLayer()!.valueForKey(event)
}
animation.delegate = self
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
return animation
}
override class func needsDisplayForKey(key: String) -> Bool {
if key == "_startAngle" || key == "_endAngle" {
return true
}
return super.needsDisplayForKey(key)
}
override func drawInContext(ctx: CGContext!) {
var center: CGPoint = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)
var radius: CGFloat = CGFloat((CGFloat(self.bounds.width) - CGFloat(_strokeWidth)) / 2)
// Set the stroke color
CGContextSetStrokeColorWithColor(ctx, _strokeColor.CGColor)
// Set the line width
CGContextSetLineWidth(ctx, _strokeWidth)
// Set the fill color (if you are filling the circle)
CGContextSetFillColorWithColor(ctx, UIColor.clearColor().CGColor)
// Draw the arc around the circle
CGContextAddArc(ctx, center.x, center.y, radius, _startAngle, _lenghtOfArc * _endAngle, 0)
// Draw the arc
CGContextDrawPath(ctx, kCGPathStroke) // or kCGPathFillStroke to fill and stroke the circle
super.drawInContext(ctx)
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
// Trigger animation for other layer by setting their values for _startAngle and _endAngle.
/*if flag && _nextLayerToAnimate != nil && _nextLayerToAnimateStartAngle != nil && _nextLayerToAnimateEndAngle != nil && _nextLayerToAnimateDuration != nil {
self._nextLayerToAnimate!._startAngle = _nextLayerToAnimateStartAngle!
self._nextLayerToAnimate!._endAngle = _nextLayerToAnimateEndAngle!
self._nextLayerToAnimate!._duration = _nextLayerToAnimateDuration!
}*/
}
但是我有一个问题,就是不知道哪一层正在制作动画,哪一层应该接替它。现在试图在以下问题中解决这个问题:
我正在尝试为正在绘制的圆制作动画,我知道该怎么做,但是,我似乎无法找到有关如何在单击按钮后触发动画的任何信息或者在服务更新数据之后。
最简单的解决方案是使用 CAShapeLayers。我需要更新时间,然后以几个圆圈的形式显示出来。
我也有能够在 drawRect 方法中执行此操作的代码,但我还不知道哪个是更新圆圈的更好解决方案。你们知道怎么做吗?
这是我在使用 CAShapeLayers 时的代码:
private var _greyCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeOneCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeTwoCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeThreeCircleLayer: CAShapeLayer = CAShapeLayer()
private var _transpartentOrangeFourCircleLayer: CAShapeLayer = CAShapeLayer()
private var _solidOrangeCircleLayer: CAShapeLayer = CAShapeLayer()
private var _currentDisplayedTime: Double = Double(0.0)
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
private func configure() {
addCAShapeLayer(self._greyCircleLayer, colour: StyleGlobals.StrokeColor.CGColor)
addCAShapeLayer(self._transpartentOrangeOneCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeTwoCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeThreeCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._transpartentOrangeFourCircleLayer, colour: StyleGlobals.TransparentOrange.CGColor)
addCAShapeLayer(self._solidOrangeCircleLayer, colour: StyleGlobals.SolidOrange.CGColor)
}
然后我尝试使用以下方法为图层设置动画,但它不起作用。它仅在将图层添加到视图层次结构后调用它时才有效:
func animateCircle(duration: NSTimeInterval, shapeLayer: CAShapeLayer, timeStart: CGFloat, timeEnd: CGFloat) {
/*//let duration = 1.0
let delay = 0.0 // delay will be 0.0 seconds (e.g. nothing)
let options = UIViewAnimationOptions.CurveEaseInOut // change the timing curve to `ease-in ease-out`
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
// any changes entered in this block will be animated
shapeLayer.strokeEnd = timeEnd
}, completion: { finished in
// any code entered here will be applied
// once the animation has completed
})*/
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
// Set the animation duration appropriately
animation.duration = duration
// Animate from 0 (no circle) to 1 (full circle)
animation.fromValue = timeStart
animation.toValue = timeEnd
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// Set the circleLayer's strokeEnd property to 1.0 now so that it's the
// right value when the animation ends.
shapeLayer.strokeEnd = timeEnd
// Do the actual animation
shapeLayer.addAnimation(animation, forKey: "strokeEnd")
}
通常会在您添加时添加 UIView
:
self.addSubView(viewToAdd);
如果您在 UIView
中插入了动画代码(不管它是哪种 UIControl
元素),您可以在 drawRect()
中触发绘图这个:
viewToAdd.setNeedsDisplay();
setNeedsDisplay
触发视图更新自身并调用永远不应直接调用的 drawRect
方法。
您也可以让视图在准备就绪时自行绘制。您可以在 UIView
中添加方法 didMoveToSuperView()
,然后调用 setNeedsDisplay()
.
这是您要找的吗?
class MyView: UIView {
override func didMoveToSuperview() {
println("Animate Me!")
}
}
所以我终于在下面的教程中找到了答案:
http://blog.pixelingene.com/2012/02/animating-pie-slices-using-a-custom-calayer/
查看 PieSliceLayer class,它显示了如何使用可观察的字段为更改设置动画。
此外,在将图层添加到 UIView class 时,您需要对其调用 setNeedsDisplay() 才能显示它。默认不显示。
下面是我的代码:
class HourLayer: CAShapeLayer {
@NSManaged private var _startAngle: CGFloat
@NSManaged private var _endAngle: CGFloat
@NSManaged private var _fillColor: UIColor
@NSManaged private var _strokeWidth: CGFloat
@NSManaged private var _strokeColor: UIColor
@NSManaged private var _duration: CFTimeInterval
private var _previousEndAngle: CGFloat = 0.0
private var _previousStartAngle: CGFloat = 0.0
private var _nextLayerToAnimate: HourLayer?
private var _nextLayerToAnimateStartAngle: CGFloat?
private var _nextLayerToAnimateEndAngle: CGFloat?
private var _nextLayerToAnimateDuration: CFTimeInterval?
private let _lenghtOfArc: CGFloat = CGFloat(2 * M_PI)
internal var StartAngle: CGFloat {
get {
return _startAngle
}
set {
_startAngle = newValue
}
}
internal var EndAngle: CGFloat {
get {
return _endAngle
}
set {
_endAngle = newValue
}
}
internal var FillColor: UIColor {
get {
return _fillColor
}
set {
_fillColor = newValue
}
}
internal var StrokeWidth: CGFloat {
get {
return _strokeWidth
}
set {
_strokeWidth = newValue
}
}
internal var StrokeColor: UIColor {
get {
return _strokeColor
}
set {
_strokeColor = newValue
}
}
internal var DurationOfAnimation: CFTimeInterval {
get {
return _duration
}
set {
_duration = newValue
}
}
required override init!() {
super.init()
self._startAngle = 0.0
self._endAngle = 0.0
self._fillColor = UIColor.clearColor()
self._strokeWidth = 4.0
self._strokeColor = UIColor.blackColor()
self._duration = 1.0
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("required init(:coder) is missing")
}
override init!(layer: AnyObject!) {
super.init(layer: layer)
if layer.isKindOfClass(HourLayer) {
var other: HourLayer = layer as HourLayer
self._startAngle = other._startAngle
self._endAngle = other._endAngle
self._fillColor = other._fillColor
self._strokeWidth = other._strokeWidth
self._strokeColor = other._strokeColor
self._duration = other._duration
}
}
override var frame: CGRect {
didSet {
self.setNeedsLayout()
self.setNeedsDisplay()
}
}
override func actionForKey(event: String!) -> CAAction! {
if event == "_startAngle" || event == "_endAngle" {
return makeAnimationForKey(event)
}
return super.actionForKey(event)
}
/*internal func prepareLayerForAnimationAfterThisLayer(layer: HourLayer, startAngle: CGFloat, endAngle: CGFloat, duration: CFTimeInterval) {
self._nextLayerToAnimate = layer
self._nextLayerToAnimateStartAngle = startAngle
self._nextLayerToAnimateEndAngle = endAngle
self._nextLayerToAnimateDuration = duration
}*/
private func makeAnimationForKey(event: String) -> CABasicAnimation {
// We want to animate the strokeEnd property of the circleLayer
let animation: CABasicAnimation = CABasicAnimation(keyPath: event)
// Set the animation duration appropriately
animation.duration = self._duration
// When no animation has been triggered and the presentation layer does not exist yet.
if self.presentationLayer() == nil {
if event == "_startAngle" {
animation.fromValue = self._previousStartAngle
self._previousStartAngle = self._startAngle
} else if event == "_endAngle" {
animation.fromValue = self._previousEndAngle
self._previousEndAngle = self._endAngle
}
} else {
animation.fromValue = self.presentationLayer()!.valueForKey(event)
}
animation.delegate = self
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
return animation
}
override class func needsDisplayForKey(key: String) -> Bool {
if key == "_startAngle" || key == "_endAngle" {
return true
}
return super.needsDisplayForKey(key)
}
override func drawInContext(ctx: CGContext!) {
var center: CGPoint = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2)
var radius: CGFloat = CGFloat((CGFloat(self.bounds.width) - CGFloat(_strokeWidth)) / 2)
// Set the stroke color
CGContextSetStrokeColorWithColor(ctx, _strokeColor.CGColor)
// Set the line width
CGContextSetLineWidth(ctx, _strokeWidth)
// Set the fill color (if you are filling the circle)
CGContextSetFillColorWithColor(ctx, UIColor.clearColor().CGColor)
// Draw the arc around the circle
CGContextAddArc(ctx, center.x, center.y, radius, _startAngle, _lenghtOfArc * _endAngle, 0)
// Draw the arc
CGContextDrawPath(ctx, kCGPathStroke) // or kCGPathFillStroke to fill and stroke the circle
super.drawInContext(ctx)
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
// Trigger animation for other layer by setting their values for _startAngle and _endAngle.
/*if flag && _nextLayerToAnimate != nil && _nextLayerToAnimateStartAngle != nil && _nextLayerToAnimateEndAngle != nil && _nextLayerToAnimateDuration != nil {
self._nextLayerToAnimate!._startAngle = _nextLayerToAnimateStartAngle!
self._nextLayerToAnimate!._endAngle = _nextLayerToAnimateEndAngle!
self._nextLayerToAnimate!._duration = _nextLayerToAnimateDuration!
}*/
}
但是我有一个问题,就是不知道哪一层正在制作动画,哪一层应该接替它。现在试图在以下问题中解决这个问题: