import UIKit
enum EWSWaveShapModel {
case circle
case rect
}
class EWSWaveLoadingView: UIView {
var originX = 0.0 //X坐标起点, the x origin of wave
var shapeModel : EWSWaveShapModel = .circle
fileprivate let cycle = 1.0 //循环次数
fileprivate let waveMoveSpan = 10.0 //波浪移动单位跨度
fileprivate let animationUnitTime = 0.05 //重画单位时间
fileprivate var term = 60.0 //周期(在代码中重新计算)
fileprivate var phasePosition = 0.0 //相位必须为0(画曲线机制局限)
fileprivate var amplitude = 10.0 //波幅
fileprivate var position = 40.0 //X轴所在的Y坐标(在代码中重新计算)
fileprivate let heavyColor = EWSRGBCOLOR(R: 38, G: 227, B: 198) //波浪重度颜色
fileprivate let lightColor = EWSRGBCOLOR(R: 121, G: 248, B: 221) //波浪轻度颜色
fileprivate let clipCircleColor = EWSRGBCOLOR(R: 232, G: 233, B: 231) //外圈颜色
fileprivate var clipCircleLineWidth: CGFloat = 2 //外圈宽度
fileprivate let progressTextFontSize: CGFloat = 15.0 //文字大小
fileprivate var waving: Bool = true
fileprivate let waveLable = UILabel()
var progress: Double = 0.5 {
didSet {
self.setNeedsDisplay()
}
}
var waveAmplitude: Double {
get {
return amplitude
}
set {
amplitude = newValue
self.setNeedsDisplay()
}
}
var borderWidth: CGFloat {
get {
return clipCircleLineWidth
}
set {
clipCircleLineWidth = newValue
self.setNeedsDisplay()
}
}
var attributeString: NSAttributedString? {
didSet {
if let attributeStr = attributeString {
waveLable.attributedText = attributeStr
}
}
}
//if use not in xib, create an func init
override func awakeFromNib() {
animationWave()
self.backgroundColor = lightColor
}
override init(frame: CGRect) {
super.init(frame: frame)
buildWaveLoadingSubView()
animationWave()
self.backgroundColor = lightColor
}
private func buildWaveLoadingSubView() {
addSubview(waveLable)
waveLable.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}
waveLable.numberOfLines = 0
waveLable.textAlignment = .center
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
override func draw(_ rect: CGRect) {
position = (1 - progress) * Double(rect.height)
//circle clip
clipWithCircle()
//draw wave
drawWaveWater(originX - term / 5, fillColor: lightColor)
drawWaveWater(originX, fillColor: heavyColor)
//Let clipCircle above the waves
clipWithCircle()
}
override func layoutSubviews() {
super.layoutSubviews()
//计算周期calculate the term
term = Double(self.bounds.size.width) / cycle
self.layer.cornerRadius = self.bounds.size.width * 0.5
self.layer.masksToBounds = true
}
override func removeFromSuperview() {
super.removeFromSuperview()
waving = false
}
func clipWithCircle() {
let circleRectWidth = min(self.bounds.size.width, self.bounds.size.height) - clipCircleLineWidth
let circleRectOriginX = (self.bounds.size.width - circleRectWidth) / 2
let circleRectOriginY = (self.bounds.size.height - circleRectWidth) / 2
let circleRect = CGRect(x: circleRectOriginX, y: circleRectOriginY, width: circleRectWidth, height: circleRectWidth)
var clipPath: UIBezierPath!
if shapeModel == .circle {
clipPath = UIBezierPath(ovalIn: circleRect)
} else if shapeModel == .rect {
clipPath = UIBezierPath(rect: circleRect)
}
clipCircleColor.setStroke()
clipPath.lineWidth = clipCircleLineWidth
clipPath.stroke()
clipPath.addClip()
}
func drawWaveWater(_ originX: Double, fillColor: UIColor) {
let curvePath = UIBezierPath()
curvePath.move(to: CGPoint(x: originX, y: position))
//循环,画波浪wave path
var tempPoint = originX
for _ in 1...rounding(4 * cycle) {//(2 * cycle)即可充满屏幕,即一个循环,为了移动画布使波浪移动,我们要画两个循环
curvePath.addQuadCurve(to: keyPoint(tempPoint + term / 2, originX: originX), controlPoint: keyPoint(tempPoint + term / 4, originX: originX))
tempPoint += term / 2
}
//close the water path
curvePath.addLine(to: CGPoint(x: curvePath.currentPoint.x, y: self.bounds.size.height))
curvePath.addLine(to: CGPoint(x: CGFloat(originX), y: self.bounds.size.height))
curvePath.close()
fillColor.setFill()
curvePath.lineWidth = 10
curvePath.fill()
}
func animationWave() {
DispatchQueue.global().async {
[weak self] () -> Void in
if self != nil {
let tempOriginX = self!.originX
while self != nil && self!.waving {
if self!.originX <= tempOriginX - self!.term {
self!.originX = tempOriginX - self!.waveMoveSpan
} else {
self!.originX -= self!.waveMoveSpan
}
DispatchQueue.main.async(execute: { () -> Void in
self!.setNeedsDisplay()
})
Thread.sleep(forTimeInterval: self!.animationUnitTime)
}
}
}
}
//determine the key point of curve
func keyPoint(_ x: Double, originX: Double) -> CGPoint {
//x为当前取点x坐标,columnYPoint的参数为相对于正弦函数原点的x坐标
return CGPoint(x: x, y: columnYPoint(x - originX))
}
func columnYPoint(_ x: Double) -> Double {
//三角正弦函数
let result = amplitude * sin((2 * Double.pi / term) * x + phasePosition)
return result + position
}
//四舍五入
func rounding(_ value: Double) -> Int {
let tempInt = Int(value)
let tempDouble = Double(tempInt) + 0.5
if value > tempDouble {
return tempInt + 1
} else {
return tempInt
}
}
}