CAShapeLayer和UIBezierPath制作一个圆形进度条

本文所有代码均为swift,但即使你只是一个OCer,我保证你也不会看不懂。文章末尾附上demo地址。

上次用CAShapeLayer和UIBezierPath画了一个企鹅(传送门),这次还是用这两样东西,封装一个简单实用的进度条工具。先上一个效果图:

CAShapeLayer和UIBezierPath制作一个圆形进度条_第1张图片
progress.gif

先来整理一下我们需要做什么,首先要有一个显示进度数字的label,还要有一个进度条,一个进度条的底,基本上就是这些。
第一步,我们需要创建一个继承UIView的子类,就像下面:

class ProgressView:UIView {
    override init(frame: CGRect) {
        super.init(frame: frame);
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

然后我们需要的属性或变量:

    /// 进度条
    private var circleLayer:CAShapeLayer!
    /// 显示进度数字的label
    private var progressLabel:UILabel!
    /// 背景条的宽度
    private let backgroundLineWidth:CGFloat = 8;
    /// 进度条的宽度,比背景条稍宽,不至于露出下面的条,丑
    private let progressLineWidth:CGFloat = 12;
    /// 进度
    var progress:CGFloat! = 0.0

然后在初始化方法中,我们先来使用UIBezierpath制作一个进度条的圆形底,但是我们要先确定进度条的半径,为了不超出这个view自身的大小,我们选宽高小的作为进度条的直径,但是UIBezierpath的半径是从圆心到CAShapeLayer的线的宽度中心点的距离,所以我们需要减去背景宽度的一半:

        let radius = (self.bounds.width > self.bounds.height ? self.bounds.height/2.0 : self.bounds.width/2.0) - backgroundLineWidth/2.0;
        let layerCenter = CGPointMake(self.bounds.width/2.0, self.bounds.height/2.0);

        // 画一个圆形的path
        let shadowBezierPath = UIBezierPath(arcCenter: layerCenter, radius: radius, startAngle: 0, endAngle: 2*CGFloat(M_PI), clockwise: true);
        let shadowLayer = CAShapeLayer();
        // 填充色
        shadowLayer.fillColor = UIColor.clearColor().CGColor;
        // 边框色
        shadowLayer.strokeColor = UIColor.blackColor().CGColor;
        shadowLayer.frame = self.bounds;
        // 边框的宽度
        shadowLayer.lineWidth = backgroundLineWidth;
        shadowLayer.path = shadowBezierPath.CGPath;
        self.layer.addSublayer(shadowLayer);

这样,我们来创建一个对象,看一下效果。

CAShapeLayer和UIBezierPath制作一个圆形进度条_第2张图片
back_Image.png

这样我们的进度条的底就算做好了,接下来我们做盖在上面的进度条,需要注意的是,即使进度是0,我们也需要展示一点进度,不然太难看。

        circleLayer = CAShapeLayer();
        circleLayer.fillColor = UIColor.clearColor().CGColor;
        circleLayer.strokeColor = UIColor(red: 214/255.0, green: 214/255.0, blue: 214/255.0, alpha: 1.0).CGColor;
        /// 这个是设置线的头部是圆角
        circleLayer.lineCap = kCALineCapRound;
        circleLayer.frame = self.bounds;
        circleLayer.lineWidth = progressLineWidth;
        self.layer.addSublayer(circleLayer);
        
        progressLabel = UILabel();
        progressLabel.font = UIFont.boldSystemFontOfSize(14);
        progressLabel.textColor = UIColor.blackColor();
        progressLabel.numberOfLines = 1;
        progressLabel.textAlignment = NSTextAlignment.Center;
        progressLabel.lineBreakMode = NSLineBreakMode.ByTruncatingTail;
        self.addSubview(progressLabel);
        
        ///  这是个记号
        self.progressLabel.text = String(format: "%.0f", self.progress*100);
        self.progressLabel.sizeToFit();
        var labFrame = self.progressLabel.frame;
        labFrame = CGRectMake((self.frame.width - self.progressLabel.frame.width)/2.0, (self.frame.height - self.progressLabel.frame.height)/2.0, self.progressLabel.frame.width, self.progressLabel.frame.height);
            self.progressLabel.frame = labFrame;
            
        var p = self.progress;
        if (self.progress < 0.01) {
                p = 0.01;
        }
        let bezierPath = UIBezierPath(arcCenter: CGPointMake(self.frame.width/2.0, self.frame.height/2.0), radius: radius, startAngle: CGFloat(-M_PI_2), endAngle: p*360/180*CGFloat(M_PI) - CGFloat(M_PI_2), clockwise: true);
        self.circleLayer.path = bezierPath.CGPath;

效果如下:

CAShapeLayer和UIBezierPath制作一个圆形进度条_第3张图片
progress_Image.png

接下来我们需要做的就是在改变progress的时候,相应的改变进度的弧度,因此,我们先把以上代码中的记号下面的代码拉出来放一个方法里,在progress的didSet方法里,调用这个方法即可,比如这个方法叫setProgress:

    /// 进度
    var progress:CGFloat! = 0.0 {
        didSet {
            if (progress >= 1) {
                progress = 1;
            }
            self.setProgress();
        }
    }

到这,这个进度条控件基本已经做好了,但是还不够完善,比如再用NSURLSession下载的代理中,因为下载的代理方法并不在主线程中,在代理方法中直接改变progress,就会有问题,我们可以把setProgress中的代码放到主线程中。

        dispatch_async(dispatch_get_main_queue()) { 
                // setProgress
        }

下篇预告:

indicator.gif

demo地址

你可能感兴趣的:(CAShapeLayer和UIBezierPath制作一个圆形进度条)