在Swift中动画自定义UIView属性 [英] Animate Custom UIView Property in Swift

查看:148
本文介绍了在Swift中动画自定义UIView属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

all!

我使用CoreGraphics创建了一个循环进度视图,其外观和更新如下:

I have created a circular progress view using CoreGraphics that looks and updates like so:

50%

75%

50% 75%

该类是一个UIView类,它有一个名为'progress'的变量,用于确定填充圆圈的数量。

The class is a UIView class, and it has a variable called 'progress' that determines how much of the circle is filled in.

它的工作原理好吧,但是我希望能够对进度变量进行动画处理,使条形图能够平滑动画。

It works well, but I want to be able to animate changes to the progress variable so that the bar animates smoothly.

我从无数的例子中读到我需要一个CALayer我已经制作了类和View类,但它根本没有动画。

I have read from myriad examples that I need to have a CALayer class along with the View class, which I have made, however, it doesn't animated at all.

两个问题:


  1. 我可以继续吗?我用Core Graphics绘制的图形,还是我需要以某种方式在CALayer中重绘它?

  2. 我当前(尝试过的)解决方案在底部崩溃:动画。 fromValue = pres.progress 。怎么了?

class CircleProgressView: UIView {
@IBInspectable var backFillColor: UIColor = UIColor.blueColor()
@IBInspectable var fillColor: UIColor = UIColor.greenColor()
@IBInspectable var strokeColor: UIColor = UIColor.greenColor()

dynamic var progress: CGFloat = 0.00 {
    didSet {
        self.layer.setValue(progress, forKey: "progress")
    }
}

var distToDestination: CGFloat = 10.0
@IBInspectable var arcWidth: CGFloat = 20
@IBInspectable var outlineWidth: CGFloat = 5

override class func layerClass() -> AnyClass {
    return CircleProgressLayer.self
}

override func drawRect(rect: CGRect) {

    var fillColor = self.fillColor

    if distToDestination < 3.0 {
        fillColor = UIColor.greenColor()
    } else {
        fillColor = self.fillColor
    }

    //Drawing the inside of the container

    //Drawing in the container
    let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
    let radius: CGFloat = max(bounds.width, bounds.height) - 10
    let startAngle: CGFloat = 3 * π / 2
    let endAngle: CGFloat = 3 * π / 2 + 2 * π
    let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    path.lineWidth = arcWidth
    backFillColor.setStroke()
    path.stroke()
    let fill = UIColor.blueColor().colorWithAlphaComponent(0.15)
    fill.setFill()
    path.fill()

    //Drawing the fill path. Same process
    let fillAngleLength =  (π) * progress
    let fillStartAngle = 3 * π / 2 - fillAngleLength
    let fillEndAngle = 3 * π / 2 + fillAngleLength

    let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true)
    fillPath_fill.lineWidth = arcWidth
    fillColor.setStroke()
    fillPath_fill.stroke()

    //Drawing container outline on top
    let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    outlinePath_outer.lineWidth = outlineWidth
    outlinePath_inner.lineWidth = outlineWidth
    strokeColor.setStroke()
    outlinePath_outer.stroke()
    outlinePath_inner.stroke()
}

}

class CircleProgressLayer: CALayer {
    @NSManaged
    var progress: CGFloat
override class func needsDisplayForKey(key: String) -> Bool {
    if key == "progress" {
        return true
    }
    return super.needsDisplayForKey(key)
}

override func actionForKey(key: String) -> CAAction? {
    if (key == "progress") {
        if let pres = self.presentationLayer() {
            let anim: CABasicAnimation = CABasicAnimation.init(keyPath: key)
            anim.fromValue = pres.progress
            anim.duration = 0.2
            return anim
        }
    return super.actionForKey(key)
    } else {
        return super.actionForKey(key)
    }
}

}

感谢您的帮助!

推荐答案

试试这个:)

class ViewController: UIViewController {

    let progressView = CircleProgressView(frame:CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200))

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton()
        button.frame = CGRectMake(0, 300, 200, 100)
        button.backgroundColor = UIColor.yellowColor()
        button.addTarget(self, action: #selector(ViewController.tap), forControlEvents: UIControlEvents.TouchUpInside)
        view.addSubview(button)
        view.addSubview(progressView)

        progressView.progress = 1.0
   }

    func tap() {
        if  progressView.progress == 0.5 {
            progressView.progress = 1.0
        } else {
            progressView.progress = 0.5
        }
    }
}

class CircleProgressView: UIView {

    dynamic var progress: CGFloat = 0.00 {
        didSet {
            let animation = CABasicAnimation()
            animation.keyPath = "progress"
            animation.fromValue = circleLayer().progress
            animation.toValue = progress
            animation.duration = Double(0.5)
            self.layer.addAnimation(animation, forKey: "progress")
            circleLayer().progress = progress
        }
    }

    func circleLayer() ->  CircleProgressLayer {
        return self.layer as! CircleProgressLayer
    }

    override class func layerClass() -> AnyClass {
        return CircleProgressLayer.self
    }
}

class CircleProgressLayer: CALayer {
    @NSManaged var progress: CGFloat

    override class func needsDisplayForKey(key: String) -> Bool {
        if key == "progress" {
            return true
        }
        return super.needsDisplayForKey(key)
    }

    var backFillColor: UIColor = UIColor.blueColor()
    var fillColor: UIColor = UIColor.greenColor()
    var strokeColor: UIColor = UIColor.greenColor()
    var distToDestination: CGFloat = 10.0
    var arcWidth: CGFloat = 20
    var outlineWidth: CGFloat = 5

    override func drawInContext(ctx: CGContext) {
        super.drawInContext(ctx)

        UIGraphicsPushContext(ctx)

        //Drawing in the container
        let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
        let radius: CGFloat = max(bounds.width, bounds.height) - 10
        let startAngle: CGFloat = 3 * CGFloat(M_PI) / 2
        let endAngle: CGFloat = 3 * CGFloat(M_PI) / 2 + 2 * CGFloat(M_PI)
        let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        path.lineWidth = arcWidth
        backFillColor.setStroke()
        path.stroke()
        let fill = UIColor.blueColor().colorWithAlphaComponent(0.15)
        fill.setFill()
        path.fill()

        //Drawing the fill path. Same process
        let fillAngleLength =  (CGFloat(M_PI)) * progress
        let fillStartAngle = 3 * CGFloat(M_PI) / 2 - fillAngleLength
        let fillEndAngle = 3 * CGFloat(M_PI) / 2 + fillAngleLength

        let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true)
        fillPath_fill.lineWidth = arcWidth
        fillColor.setStroke()
        fillPath_fill.stroke()

        //Drawing container outline on top
        let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        outlinePath_outer.lineWidth = outlineWidth
        outlinePath_inner.lineWidth = outlineWidth
        strokeColor.setStroke()
        outlinePath_outer.stroke()
        outlinePath_inner.stroke()

        UIGraphicsPopContext()
    }
}

这篇关于在Swift中动画自定义UIView属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆