【问题标题】:How to trigger an animation after adding the view to the view hierarchy将视图添加到视图层次结构后如何触发动画
【发布时间】:2015-03-06 08:09:50
【问题描述】:

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

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

【问题讨论】:

  • 使用 CAAnimation 缩放 CAShapeLayer
  • 我添加了代码来证明我知道如何为图层设置动画,只是动画在其他地方调用 animateCircle 方法时什么都不做,而不仅仅是将图层添加到视图层次结构中初始化函数。

标签: ios swift animation cashapelayer


【解决方案1】:

通常UIView 会在您添加时添加:

self.addSubView(viewToAdd);

如果您在UIView 中插入了动画代码(不管它是哪种UIControl 元素),您可以通过以下方式触发drawRect() 中的绘图:

viewToAdd.setNeedsDisplay();

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

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

【讨论】:

  • 所以如果我要更新我的 UI 控件中的时间,我只能通过调用方法 setNeedsDisplay() 来刷新它,当代码使用正确的方法时?
  • 你说什么时间?我认为你最好发布你用来包含应该在父视图中动画的视图的代码。
  • 哦,对不起,我画的圆圈代表时间,所以当我更新时间时,我需要刷新或重绘视图。视图就是您在上面看到的类。这是我子类化的 UIView。在这个 UIView 中,我有多个代表小时和分钟的图层。 UIView 的子类正在通过 addSubView 方法添加到 UITableViewCell。
  • 但是你为什么要把时间设置在你的视野之外呢?它是用于动画还是您需要它的原因是什么?
  • 好吧,我想向用户展示他们必须等待的时间。这个时间必须每分钟更新一次。这必须在 tableviewcell 中完成。因此,即使用户只是查看单元格而不是滚动表格视图,单元格也必须根据我从远程服务器获得的时间刷新多个圆形视图。
【解决方案2】:

这就是你要找的吗?

class MyView: UIView {

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

}

【讨论】:

    【解决方案3】:

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      相关资源
      最近更新 更多