漸變色進度條的兩種繪製方案

滴水微瀾發表於2018-12-26
在App開發中經常會用到漸變色進度條控制元件,而自定義進度條的實現也不難,下面提供了兩種漸變色進度條的實現方案。
 
效果圖如下:

 

第一種實現方案:使用圖層layer實現

層級結構如圖所示:

 

構建過程如下: 

1.建立容器
容器建立方案上採用的是生成UIView的子檢視:LabelProgressBar,
把LabelProgressBar當作一個類似容器的控制元件而面向客戶端。
好處是:可以方便的使用LabelProgressBar在Xib,StoryBoard,程式碼中。
而如果用其他的方式,卻做不到這樣使用上的靈活。
 
2.新增漸變色圖層到容器
新增漸變色到容器就比較容易了,程式碼如下:

//新增漸變色圖層gradientLayer
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor(hex: "4DABF4").cgColor, UIColor(hex: "9B30C1").cgColor]
//(I.e. [0,0] is the bottom-left corner of the layer, [1,1] is the top-right corner.)
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
gradientLayer.position = CGPoint(x: width*0.5, y: height*0.5)
gradientLayer.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: width, height: height))
self.layer.insertSublayer(gradientLayer, at: 0)

3.新增文字檢視到容器
新增文字檢視到容器也是常規操作,程式碼如下:

 

 lazy var contextLabel: UILabel = {
        let label = UILabel()
        label.text = " %"
        label.font = UIFont.systemFont(ofSize: 12)
        label.textColor = UIColor.white
        label.textAlignment = NSTextAlignment.center
        label.backgroundColor = UIColor.clear
        return label
    }()

 contextLabel.frame = CGRect(x: 0, y: 0, width: width, height: height)
self.addSubview(contextLabel)
 
進度變化過程
 
1.根據進度比例生成一個貝塞爾曲線
2.建立一個CALayer圖層,將進度貝塞爾曲線賦值給它的path屬性
3.拿到漸變色圖層,將貝塞爾曲線圖層賦值給它的mask屬性
func maskLayer() {
    let temp = CGPoint(x: 0, y: 0)
    let bez = UIBezierPath()
    bez.move(to: temp)
    bez.addLine(to: CGPoint(x: progressWidth, y: 0))
    bez.addArc(withCenter: CGPoint(x: progressWidth, y: height*0.5), radius: height*0.5, startAngle: -CGFloat(M_PI_2), endAngle: CGFloat(M_PI_2), clockwise: true)
    bez.addLine(to: CGPoint(x: 0, y: height))
    bez.close()
    
    msLayer.path = bez.cgPath
    bottomLayer!.mask = msLayer
}
4.不同重複1-3的過程
這四個步驟也是不斷呼叫maskLayer()方法的過程
 
第一種實現方案:使用CoreGraphics實現
 
主要實現思想:使用CoreGraphics,不斷繪製
構建過程:
1.建立UIView的子類PartArcView
2.填充UIView提供的鉤子函式func draw(_ rect: CGRect),draw方法內的實現為:
  a.在當前上下文中,從左到右繪製滿線性漸變色內容
  b.繪製中間為鏤空半圓弧的白色矩形內容,鋪滿上下文
  c.在圓弧的右半部分繪製灰色遮蓋弧,去覆蓋漸變色圓弧。達到進度不斷變化的效果。
 
注意點:畫弧時順時針方向問題,上下文出棧入棧問題
 
上下文出棧入棧問題:
上下文物件是個單例物件,裡面儲存的是當前繪製皮膚的各種屬性設定,包括(線色,線寬,填充色,折線圓角等)
如果不對當前上下文儲存,就做修改,會修改整體屬性,造成對下面繪製的汙染。所以在繪製一段影象前先將當前上下文屬性
入棧,等繪製完成後,在將剛才的原始上下文屬性出棧,設定到上下文單例中來。如:程式碼中的繪製漸變色弧和灰色弧部分就
用到了上下文的出棧入棧操作(context?.saveGState(), context?.restoreGState())

畫弧時順時針方向問題:

CoreGraphics的座標系弧度,與順時針方向如圖所示,注意不要用錯了:

若要詳情瞭解的話,請參考之前介紹過的一篇文章:https://www.cnblogs.com/zhou--fei/p/9859244.html

 

3.將進度值賦值PartArcView屬性
4.呼叫setNeedsDisplay()
5.不斷重複3-4步驟
部分程式碼如下:
let context = UIGraphicsGetCurrentContext()
//漸變色
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations:[CGFloat] = [0,1]
let startC = UIColor(hex: "EEA13A")
let endC = UIColor(hex: "B1283C")
let colors = [startC.cgColor,endC.cgColor]
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations)
context?.drawLinearGradient(gradient!, start: CGPoint(x: 0, y: 0), end: CGPoint(x: rect.size.width, y: 0), options: CGGradientDrawingOptions.drawsBeforeStartLocation)

//漸變色弧
context?.saveGState()
context?.addRect(rect)
context?.move(to: CGPoint(x: width-edgeDistance, y: bigOuterRadius))
context?.addArc(center: arcCenter, radius: bigOuterRadius, startAngle: 0, endAngle: CGFloat(M_PI), clockwise: true)
context?.addArc(center: CGPoint(x: smailRadius+edgeDistance, y: arcCenter.y), radius: smailRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI*2), clockwise: true)
context?.addArc(center: arcCenter, radius: bigInnerRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI*2), clockwise: false)
context?.addArc(center: CGPoint(x: width-smailRadius-edgeDistance, y: arcCenter.y), radius: smailRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI*2), clockwise: true)
context?.setFillColor(UIColor.white.cgColor)
context?.fillPath()


//灰色弧
context?.restoreGState()
let context1 = UIGraphicsGetCurrentContext()
var endAng =  CGFloat(M_PI*2) - (_progressValue * CGFloat(M_PI))
context1?.move(to: CGPoint(x: width-edgeDistance, y: bigOuterRadius))
context1?.addArc(center: arcCenter, radius: bigOuterRadius, startAngle: 0, endAngle: endAng, clockwise: true)

let midSmallX: CGFloat = arcCenter.x + cos(endAng)*(bigOuterRadius-smailRadius)
let midSmallY: CGFloat = arcCenter.y + sin(endAng)*(bigOuterRadius-smailRadius)

context1?.addArc(center: CGPoint(x: midSmallX, y: midSmallY), radius: smailRadius, startAngle: endAng, endAngle: endAng-CGFloat(M_PI), clockwise: false)

context1?.addArc(center: arcCenter, radius: bigInnerRadius, startAngle: endAng, endAngle: CGFloat(M_PI*2), clockwise: false)
context1?.addArc(center: CGPoint(x: width-smailRadius-edgeDistance, y: arcCenter.y), radius: smailRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI*2), clockwise: true)
context1?.setFillColor(UIColor(hex: "e7e3e3").cgColor)
context1?.fillPath()
點選首頁列表的進度條目錄進入

 

相關文章