Swift4擼一個圓形漸變進度條

最近項目需求里有一個環(huán)形漸變進度條。在查閱相關(guān)資料后,決定自己擼一個茧球,當(dāng)然感謝ios 做一個完整的漸變進度條(OC原版)文章作者提供的思路和Demo。

首先星持,上效果圖:
效果圖.gif

思路

利用layer層的遮罩完成整個功能抢埋。
實現(xiàn)步驟:
1、繪制環(huán)形路徑(貝塞爾曲線實現(xiàn))
2、繪制背景環(huán)狀layer層揪垄,添加曲線路徑穷吮。
3、繪制漸變色layer層饥努,添加到view的子layer上捡鱼。
4、繪制進度layer層酷愧,添加曲線路徑(用shapeLayer的strokeEnd屬性來控制進度的結(jié)尾處)驾诈。
利用到的主要的功能類有:
UIBezierPath: 繪制環(huán)形的主要功能類
CAShapeLayer: 主要結(jié)合貝塞爾曲線進行各種形狀的繪制類
CAGradientLayer: 渲染漸變色的主要功能類
UIView: 這個就不多說了


代碼實現(xiàn)

首先創(chuàng)建View類用于自定義視圖:


類中包含的屬性以及方法.png

1、開始添加可能用到的屬性
屬性:

var progess: CGFloat = 0.0 // 環(huán)形進度
var label: UILabel? // 中心文本顯示
var lineWidth: CGFloat = 0.0 // 環(huán)形的寬
private var foreLayer: CAShapeLayer? // 進度條的layer層(可做私有屬性)

2溶浴、添加完屬性后乍迄,首先開始覆寫父類的構(gòu)造器,并自定義自己的便利構(gòu)造器(這里需要注意的是在swift中本類的便利構(gòu)造器只能調(diào)用本類的構(gòu)造器戳葵,本類的構(gòu)造器里,只能調(diào)用父類的構(gòu)造器進行覆寫)

關(guān)于構(gòu)造器的覆寫的概念汉匙,上述是我自己的理解拱烁,我本人也不是太明確,如果有明白的讀者發(fā)現(xiàn)我的理解有誤噩翠,請不吝指正 戏自,非常感謝。

首先覆寫父類的構(gòu)造器:

override init(frame: CGRect) {
    super.init(frame: frame)
}
// 覆寫父類構(gòu)造器后這個方法是必須實現(xiàn)的
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

然后伤锚,編寫自己的遍歷構(gòu)造器

// 遍歷構(gòu)造器傳入frame擅笔,以及進度條寬度
convenience init(frame: CGRect, lineWidth: CGFloat) {
    self.init(frame: frame)
    self.lineWidth = lineWidth
    seup(rect: frame) // 繪制自定義視圖的函數(shù)
}

最后,實現(xiàn)seup函數(shù):

func seup(rect: CGRect) -> Void {
        // 背景圓環(huán)(灰色背景)
        let shapeLayer: CAShapeLayer = CAShapeLayer.init()
        // 設(shè)置frame
        shapeLayer.frame = CGRect.init(x: 0, y: 0, width: rect.size.width, height: rect.size.height)
        shapeLayer.lineWidth = self.lineWidth
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.init(red: 50/255, green: 40/255, blue: 50/255, alpha: 1).cgColor

        let center: CGPoint = CGPoint.init(x: rect.size.width/2, y: rect.size.height/2)
        // 畫出曲線(貝塞爾曲線)
        let bezierPath: UIBezierPath = UIBezierPath.init(arcCenter: center, radius: (rect.size.width - self.lineWidth)/2, startAngle: CGFloat(-0.5 * Double.pi), endAngle: CGFloat(1.5 * Double.pi), clockwise: true)
        shapeLayer.path = bezierPath.cgPath // 將曲線添加到layer層

        self.layer.addSublayer(shapeLayer) // 添加蒙版

        // 漸變色 加蒙版 顯示蒙版區(qū)域
        let gradientLayer: CAGradientLayer = CAGradientLayer.init()

        gradientLayer.frame = self.bounds
        gradientLayer.colors = NSArray.init(array: [UIColor.init(hexString: "#5F98FC").cgColor, UIColor.init(hexString: "#47BF00").cgColor]) as? [Any]
        gradientLayer.startPoint = CGPoint.init(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint.init(x: 0, y: 1)

        self.layer.addSublayer(gradientLayer) // 將漸變色添加帶layer的子視圖

        self.foreLayer = CAShapeLayer.init()
        self.foreLayer?.frame = self.bounds
        self.foreLayer?.fillColor = UIColor.clear.cgColor
        self.foreLayer?.lineWidth = self.lineWidth
        
        self.foreLayer?.strokeColor = UIColor.red.cgColor
        self.foreLayer?.strokeEnd = 0
        /* The cap style used when stroking the path. Options are `butt', `round'
        * and `square'. Defaults to `butt'. */
        self.foreLayer?.lineCap = kCALineCapRound // 設(shè)置畫筆
        self.foreLayer?.path = bezierPath.cgPath
        // 修改漸變layer層的遮罩, 關(guān)鍵代碼
        gradientLayer.mask = self.foreLayer

        self.label = UILabel.init(frame: self.bounds)
        self.label?.text = ""
        self .addSubview(self.label!)
        self.label?.font = UIFont.boldSystemFont(ofSize: 42)
        self.label?.textColor = UIColor.white
        self.label?.textAlignment = NSTextAlignment.center
    }

至此屯援,我們初步實現(xiàn)了整個視圖的繪制猛们,調(diào)整進度的關(guān)鍵在于,foreLayer的strokeEnd屬性的值狞洋,范圍在0到1之間弯淘。

接下來,我們實現(xiàn)函數(shù)setProgress(value:)的方法吉懊,來暴露設(shè)置百分比的對外接口庐橙,如下;

func setProgress(value: CGFloat) -> Void {
    progess = value // 設(shè)置當(dāng)前屬性的值
    self.label?.text = String.init(format: "%.f%%", progess * 100) // 設(shè)置內(nèi)部Label顯示的值(注意字符的格式化)
    self.foreLayer?.strokeEnd = progess // 視圖改變的關(guān)鍵代碼
}

OK 這樣在視圖的環(huán)節(jié),我們的所有功能就實現(xiàn)了借嗽,接下來态鳖,在ViewController中,添加一個sliderbar實驗一下我們的視圖(以下代碼均在ViewDidLoad中恶导, self是當(dāng)前的ViewController)

    self.view.backgroundColor = UIColor.black
    //添加進度視圖浆竭,這里是VC的一個屬性
    self.pathView = LXCircle.init(frame: CGRect.init(x: 0, y: 0, width: self.view.bounds.size.width - 30, height: self.view.bounds.size.width - 30), lineWidth: 30)
    pathView?.center = self.view.center
    pathView?.backgroundColor = UIColor.clear

    self.view .addSubview(pathView!)

    let slider: UISlider = UISlider.init(frame: CGRect.init(x: 50, y: (pathView?.frame.maxY)! + 50, width: self.view.bounds.size.width - 2*50, height: 30))
    slider.addTarget(self, action: #selector(sliderMethod(slider:)), for: UIControlEvents.valueChanged)
    slider.maximumValue = 1.0
    slider.minimumValue = 0
    slider.minimumTrackTintColor = UIColor.init(hexString: "#5F98FC")
    self.view.addSubview(slider)

添加滑動方法:

    @objc
    func sliderMethod(slider: UISlider) -> Void {
        self.pathView?.setProgress(value: CGFloat(slider.value))
    }

最終就實現(xiàn)了如效果圖所示的Demo。


補充和完善(非常重要)

上文中在UIColor中的init(hexString:)的方法,并非是系統(tǒng)的方法兆蕉,而是我自己寫的將16進制的字符串顏色羽戒,轉(zhuǎn)化為color的方法,寫在了UIColor的擴展里(這里和objective-c中的類目的用法一樣虎韵,但概念上是有很大區(qū)別的)

具體的實現(xiàn)大家可以自行g(shù)oogle易稠,有很多實現(xiàn)方法,在這里就不贅述了包蓝。

另外驶社,上文中,如果需要給視圖傳遞一個初始化的進度测萎,需要在seup函數(shù)中做如下修改即可:

// self.foreLayer?.strokeEnd = 0
self.foreLayer?.strokeEnd = self.progess

至于為什么亡电,我想大家應(yīng)該一眼就可以看明白。


最后硅瞧,關(guān)于layer層的遮罩的順序份乒,以及利用Quartz2D去實現(xiàn)相同功能的視圖,我還是不是非常清楚腕唧,希望有讀者看到這個最后的補充有興趣我們可以多交流下或辖。

最后的最后,附上一篇在簡書的社區(qū)里發(fā)現(xiàn)的利用Quart2D實現(xiàn)環(huán)形進度的文章枣接。

簡書傳送門《iOS_Quartz2D_環(huán)形進度條繪制》

文章對你有幫助的話颂暇,請點贊喲!5獭耳鸯!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市膀曾,隨后出現(xiàn)的幾起案子县爬,更是在濱河造成了極大的恐慌,老刑警劉巖添谊,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捌省,死亡現(xiàn)場離奇詭異,居然都是意外死亡碉钠,警方通過查閱死者的電腦和手機纲缓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喊废,“玉大人祝高,你說我怎么就攤上這事∥劭辏” “怎么了工闺?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵乍赫,是天一觀的道長。 經(jīng)常有香客問我陆蟆,道長雷厂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任叠殷,我火速辦了婚禮改鲫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘林束。我一直安慰自己像棘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布壶冒。 她就那樣靜靜地躺著缕题,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胖腾。 梳的紋絲不亂的頭發(fā)上烟零,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音咸作,去河邊找鬼锨阿。 笑死每瞒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闺金。 我是一名探鬼主播虚青,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼负蚊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤酵使,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后焙糟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體口渔,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年穿撮,在試婚紗的時候發(fā)現(xiàn)自己被綠了缺脉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡悦穿,死狀恐怖攻礼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栗柒,我是刑警寧澤礁扮,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響太伊,放射性物質(zhì)發(fā)生泄漏雇锡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一僚焦、第九天 我趴在偏房一處隱蔽的房頂上張望锰提。 院中可真熱鬧,春花似錦叠赐、人聲如沸欲账。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赛不。三九已至,卻和暖如春罢洲,著一層夾襖步出監(jiān)牢的瞬間踢故,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工惹苗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留殿较,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓桩蓉,卻偏偏與公主長得像淋纲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子院究,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容