自定义控件的实现
import UIKit
fileprivate let startAngle = CGFloat(Double.pi / -2.0)
class CircleProgressView: UIView {
typealias FinishBlock = ()->Void
var finishBlock: FinishBlock?
private lazy var bgLayer: CAShapeLayer = CAShapeLayer()
private lazy var progressLayer: CAShapeLayer = CAShapeLayer()
private lazy var timeLabel: UILabel = UILabel()
private lazy var timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.global())
private var duration: Double = 0
private var progress: Double = 0
convenience init(frame: CGRect, duration: Double) {
self.init(frame: frame)
self.duration = duration
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
// fillColor 用于背景颜色填充, strokeColor 用于线条颜色
bgLayer.fillColor = UIColorHex(hex: 0xFFFFFF, alpha: 0.25).cgColor
layer.addSublayer(bgLayer)
progressLayer.fillColor = nil
progressLayer.strokeColor = UIColorHex(hex: 0xFFFFFF).cgColor
progressLayer.lineWidth = 4.0
layer.addSublayer(progressLayer)
timeLabel.textColor = .white
timeLabel.text = "\(Int(duration))s"
addSubview(timeLabel)
}
// 布局 resize 时会触发该方法
override func layoutSubviews() {
super.layoutSubviews()
let radius = bounds.height / 2.0
let endAngle = startAngle + CGFloat(Double.pi * 2 * progress)
// 画圆/扇形
bgLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
// 画圆弧
progressLayer.path = UIBezierPath(arcCenter: CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0),
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true).cgPath
timeLabel.translatesAutoresizingMaskIntoConstraints = false
timeLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
timeLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
public func updateWithProgress(currentProgress: Double, animated: Bool) {
progress = currentProgress
if animated {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 0
animation.toValue = 1
progressLayer.add(animation, forKey: nil)
}
timer.schedule(deadline: .now(), repeating: .seconds(1))
timer.setEventHandler(handler: {
DispatchQueue.main.async { [weak self] in
if self!.duration <= 0 {
self?.timer.cancel()
self?.finishBlock?()
}
self!.duration -= 1
let textDuration = (self?.duration ?? 0) + 1
self?.timeLabel.text = "\(Int(textDuration))s"
}
})
timer.resume()
}
}
使用
import UIKit
class AdvertiseViewController: BaseViewController {
var circleProgressView = CircleProgressView()
override func viewDidLoad() {
super.viewDidLoad()
circleProgressView = CircleProgressView(frame: CGRect(x: SCREEN_W - 20 - 50, y: STATUS_BAR_H, width: 50, height: 50), duration: 5)
view.addSubview(circleProgressView)
circleProgressView.updateWithProgress(currentProgress: 1, animated: true)
weak var weakself = self
circleProgressView.finishBlock = {
weakself?.switchRootController()
}
}
func switchRootController() {
}
}