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

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

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

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);

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

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;

效果如下:

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地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容