如何用渐变动画阴影?

时间:2017-02-25 07:28:12

标签: swift animation gradient shadow

我有一张如下图表:

enter image description here

我遇到动画和阴影问题。 我用动画画一个渐变,但从一开始我就有了我不想要的阴影和遮罩层,我希望阴影用渐变来制作动画。 带有动画的当前图表如下所示。 enter image description here

我不希望用户从头开始看到阴影和遮罩层。

这是我的代码:

import Foundation
import UIKit

@IBDesignable class CircularProgressView: UIView {

@IBInspectable var containerCircleColor: UIColor = UIColor.lightGray
@IBInspectable var gradientStartColor: UIColor = UIColor.green
@IBInspectable var gradientEndColor: UIColor = UIColor.yellow
@IBInspectable var arcWidth: CGFloat = 20


override init(frame: CGRect) {
    super.init(frame: frame)
    circularProgressView_init()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    circularProgressView_init()
}


fileprivate func circularProgressView_init() {

    let viewHeight = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1, constant: 0)
    self.addConstraint(viewHeight)

}

override func prepareForInterfaceBuilder() {
    circularProgressView_init()
}

override func draw(_ rect: CGRect) {
    let width = self.bounds.width
    let center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
    let radius: CGFloat = (width - (arcWidth * 2.5)) / 2
    let progressStartAngle: CGFloat = 3 * CGFloat.pi / 2
    let progressEndAngle: CGFloat = CGFloat.pi / 2

    //fill circular
    let circlePath = UIBezierPath(arcCenter:  center,
                                  radius: radius,
                                  startAngle: 0,
                                  endAngle: 360,
                                  clockwise: true)
    circlePath.lineWidth = arcWidth
    containerCircleColor.setStroke()
    circlePath.stroke()


    //MARK: ProgressPath
    let progressPath = UIBezierPath(arcCenter: center,
                                    radius: radius,
                                    startAngle: progressStartAngle,
                                    endAngle: progressEndAngle,
                                    clockwise: true)
    progressPath.lineWidth = arcWidth
    progressPath.lineCapStyle = .round

    //MARK: Gradient
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [gradientStartColor.cgColor , gradientEndColor.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0)
    gradientLayer.endPoint = CGPoint(x:1, y:1)
    gradientLayer.frame = self.bounds

    //MARK: Animation
    let anim = CABasicAnimation(keyPath: "strokeEnd")
    anim.duration = 2
    anim.fillMode = kCAFillModeForwards
    anim.fromValue = 0
    anim.toValue = 1

    //MARK: Mask Layer
    let maskLayer = CAShapeLayer()
    maskLayer.path = progressPath.cgPath
    maskLayer.fillColor = UIColor.clear.cgColor
    maskLayer.strokeColor = UIColor.black.cgColor
    maskLayer.lineWidth = arcWidth
    maskLayer.lineCap = kCALineCapRound

    gradientLayer.mask = maskLayer

    self.layer.insertSublayer(gradientLayer, at: 0)

    let context = UIGraphicsGetCurrentContext()
    let shadow = UIColor.lightGray
    let shadowOffset = CGSize(width: 3.1, height: 3.1)
    let shadowBlurRadius: CGFloat = 5
    context!.saveGState()
    context!.setShadow(offset: shadowOffset, blur: shadowBlurRadius,  color: (shadow as UIColor).cgColor)
    progressPath.stroke()
    context?.restoreGState()

    maskLayer.add(anim, forKey: nil)
    gradientLayer.add(anim, forKey: nil)

  }
}

有可能吗? 如果不是,我怎么能至少隐藏阴影和面具并在动画结束后显示它?

2 个答案:

答案 0 :(得分:1)

您应该创建另一个图层来提供阴影。

1 - 首先你必须创建一个UIView作为shadowLayer

2 - 然后屏蔽此shadowLayer,其路径和图层与您已创建的maskLayer(在您的代码中)相同。例如:shadowMaskLayer

3 - 将阴影属性添加到此新shadowMaskLayer

4 - 然后将shadowLayer添加到原始UIView CircularProgressView

5 - 同时将您已有的动画添加到此shadowLayer,以使整个圆圈为阴影设置动画。

不要犹豫提问;)

答案 1 :(得分:1)

好吧,我在这里提出了正确的答案,也许有人将来需要它。

import Foundation
import UIKit

@IBDesignable class CircularProgressView: UIView {

@IBInspectable var containerCircleColor: UIColor = UIColor.lightGray
@IBInspectable var gradientStartColor: UIColor = UIColor.yellow
@IBInspectable var gradientEndColor: UIColor = UIColor.red
@IBInspectable var arcWidth: CGFloat = 20
@IBInspectable var progressPercent: CGFloat = 50

override init(frame: CGRect) {
    super.init(frame: frame)
    circularProgressView_init()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    circularProgressView_init()
}

fileprivate func circularProgressView_init() {
    let viewHeight = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1, constant: 0)
    self.addConstraint(viewHeight)
}

override func prepareForInterfaceBuilder() {
    circularProgressView_init()
}

override func draw(_ rect: CGRect) {
    let width = self.bounds.width
    let center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
    let radius: CGFloat = (width - (arcWidth * 2.5)) / 2
    let progressStartAngle: CGFloat = 3 * CGFloat.pi / 2
    let progressEndAngle: CGFloat = ConvertToTrigonometry.shared.trigonimetryCordinate(percentage: progressPercent) //CGFloat.pi / 2

    //fill circular
    let circlePath = UIBezierPath(arcCenter:  center,
                                  radius: radius,
                                  startAngle: 0,
                                  endAngle: 360,
                                  clockwise: true)
    circlePath.lineWidth = arcWidth
    containerCircleColor.setStroke()
    circlePath.stroke()


    //MARK: ProgressPath
    let progressPath = UIBezierPath(arcCenter: center,
                                    radius: radius,
                                    startAngle: progressStartAngle,
                                    endAngle: progressEndAngle,
                                    clockwise: true)
    progressPath.lineWidth = arcWidth
    progressPath.lineCapStyle = .round

    //MARK: Gradient
    let gradientLayer = CAGradientLayer()
    gradientLayer.colors = [gradientStartColor.cgColor , gradientEndColor.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0)
    gradientLayer.endPoint = CGPoint(x:1, y:1)
    gradientLayer.frame = self.bounds

    //MARK: Mask Layer
    let maskLayer = CAShapeLayer()
    maskLayer.path = progressPath.cgPath
    maskLayer.fillColor = UIColor.clear.cgColor
    maskLayer.backgroundColor = UIColor.black.cgColor
    maskLayer.strokeColor = UIColor.black.cgColor
    maskLayer.lineWidth = arcWidth
    maskLayer.lineCap = kCALineCapRound
    maskLayer.masksToBounds = false

    gradientLayer.mask = maskLayer

    //MARK: Shadow
    let shadowLayer = CAShapeLayer()
    shadowLayer.frame = bounds
    shadowLayer.backgroundColor = UIColor.gray.cgColor
    layer.addSublayer(shadowLayer)

    let maskShadowLayer = CAShapeLayer()
    maskShadowLayer.path = progressPath.cgPath
    maskShadowLayer.fillColor = UIColor.clear.cgColor
    maskShadowLayer.backgroundColor = UIColor.black.cgColor
    maskShadowLayer.strokeColor = UIColor.black.cgColor
    maskShadowLayer.lineWidth = arcWidth
    maskShadowLayer.lineCap = kCALineCapRound
    maskShadowLayer.masksToBounds = false
    maskShadowLayer.shadowColor = UIColor.black.cgColor
    maskShadowLayer.shadowOpacity = 0.5
    maskShadowLayer.shadowOffset = CGSize(width: 3.1, height: 3.1)

    shadowLayer.mask = maskShadowLayer

    //MARK: Animation
    let anim = CABasicAnimation(keyPath: "strokeEnd")
    anim.duration = 2
    anim.fillMode = kCAFillModeForwards
    anim.fromValue = 0
    anim.toValue = 1

    maskShadowLayer.add(anim, forKey: nil)
    maskLayer.add(anim, forKey: nil)
    gradientLayer.add(anim, forKey: nil)

    layer.addSublayer(gradientLayer)

 }

}

还有一个帮助类,我用它来进行三角转换:

import Foundation
import UIKit

class ConvertToTrigonometry {

static let shared = ConvertToTrigonometry()

func trigonimetryCordinate(percentage: CGFloat) -> CGFloat {
    let pi = CGFloat.pi
    let trigonometryRatio = (percentage * 360) / 100 // How much you want to move forward in axis.
    let endPointDegree = (3 * pi / 2) + ((trigonometryRatio * 2 / 360) * pi) // End point on axis based on your trigonometryRatio and the start point which is 3pi/2
    return endPointDegree
 }
}

此解决方案绘制带有动画渐变和阴影的圆弧。 你可以在Github找到完整的项目。