2017-02-25 102 views
1

我有一个图如下图所示:如何用渐变动画阴影?

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) 

    } 
} 

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

回答

1

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

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

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

3 - 阴影属性添加到这个新的shadowMaskLayer

4 - 然后添加shadowLayer到原来的UIView CircularProgressView

5 - 还可以添加你已经有这个shadowLayer动画动画整个圆圈的阴影。

不要犹豫发问;)

+1

谢谢,这是一个好主意。我会改变我的代码并分享结果。 – Mina

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中找到完整的项目。