最近項目需求里有一個環(huán)形漸變進度條。在查閱相關(guān)資料后,決定自己擼一個茧球,當(dāng)然感謝ios 做一個完整的漸變進度條(OC原版)文章作者提供的思路和Demo。
首先星持,上效果圖:思路
利用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類用于自定義視圖:
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獭耳鸯!