如何在将视图添加到视图层次结构后触发动画

时间:2015-03-06 08:09:51

标签: ios swift animation cashapelayer

我试图为正在绘制的圆圈设置动画,我知道该怎么做,但是,在单击按钮后,我似乎无法找到有关如何触发动画的任何信息或者在服务更新数据之后。

最简单的解决方案是使用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")
}

3 个答案:

答案 0 :(得分:0)

通常会在您添加时添加UIView

self.addSubView(viewToAdd);

如果您已在UIView中插入了动画的代码(不管它是什么类型的UIControl元素),您可以在drawRect()内触发绘图这样:

viewToAdd.setNeedsDisplay();

setNeedsDisplay触发视图更新自身并调用永远不应该直接调用的drawRect方法。

您也可以让视图在准备就绪时自行绘制。您可以在didMoveToSuperView()中添加方法UIView,然后调用setNeedsDisplay()

答案 1 :(得分:0)

这是你正在寻找的吗?

class MyView: UIView {

    override func didMoveToSuperview() {
        println("Animate Me!")
    }

}

答案 2 :(得分:0)

所以我终于在以下教程中找到了答案:

http://blog.pixelingene.com/2012/02/animating-pie-slices-using-a-custom-calayer/

查看PieSliceLayer类,它显示了如何使用可观察字段为变化设置动画。

此外,在将图层添加到UIView类时,您需要在其上调用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!
    }*/
}
然而,我有一个问题就是不知道哪个图层是动画的,哪个图层应该成功。现在试着在以下问题中解决这个问题:

Swift: Animate layers in sequence from UIView