我正在开发一个速度测试应用程序,它有一个自定义
CircularProgressView
,其中包括 speedLabel
,并且 speedLabel
位于进度视图的中心。
我有一个
createCircularPath
方法来创建进度视图的路径,我在 layoutSubviews
内部调用它。
当我在自定义视图的
speedLabel
中配置 init
并运行速度测试时,speedLabel
文本和 progressTrack
将在测试运行时按预期更新。但是,如果我将 speedLabel
中的 layoutSubviews
与圆形路径一起配置,那么它们都不会被更新。
我尝试通过更改 z 分数来玩它。我还尝试将圆形路径、progressLayer 和 trackLayer 嵌入到另一个视图中,然后将视图和标签添加为子视图,但在
layoutSubviews
内配置时,进度和速度在速度测试期间仍然没有更新。
寻求解释为什么会这样。
工作进度的屏幕截图(当在
speedLabel
中配置init
时):
当在
speedLabel
中配置layoutSubviews
时,标签和进度保持静态。下面是在 layoutSubviews
中配置时的代码
import UIKit
class CircularProgressView: UIView {
var progressLayer = CAShapeLayer()
var trackLayer = CAShapeLayer()
var speedLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func layoutSubviews() {
super.layoutSubviews()
createCircularPath()
configureSpeedLabel()
}
var progressColor = UIColor.white {
didSet {
progressLayer.strokeColor = progressColor.cgColor
}
}
var trackColor = UIColor.white {
didSet {
trackLayer.strokeColor = trackColor.cgColor
}
}
var speed = 0 {
didSet {
speedLabel.text = String(speed)
}
}
fileprivate func configureSpeedLabel() {
speedLabel.translatesAutoresizingMaskIntoConstraints = false
speedLabel.text = String(speed)
speedLabel.font = UIFont(name: "TimesNewRomanPSMT", size: 24)
speedLabel.textColor = UIColor.black
speedLabel.isOpaque = true
self.addSubview(speedLabel)
NSLayoutConstraint.activate([
speedLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor),
speedLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor)
])
}
fileprivate func createCircularPath() {
self.backgroundColor = UIColor.clear
self.layer.cornerRadius = self.frame.width/2
let path = UIBezierPath(arcCenter: CGPoint(x: frame.width/2, y: frame.height/2), radius: (frame.width - 1.5)/2, startAngle: CGFloat(-0.5 * .pi), endAngle: CGFloat(1.5 * .pi), clockwise: true)
trackLayer.path = path.cgPath
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.strokeColor = trackColor.cgColor
trackLayer.opacity = 0.3
trackLayer.lineWidth = 10.0
trackLayer.strokeEnd = 1.0
self.layer.addSublayer(trackLayer)
progressLayer.path = path.cgPath
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = progressColor.cgColor
progressLayer.lineWidth = 10.0
progressLayer.strokeEnd = 0.0
self.layer.addSublayer(progressLayer)
}
}
从
layoutSubviews()
添加子视图和图层是一个非常糟糕的主意。
当我们初始化视图时,我们要配置标签属性并将其添加为子视图,并配置图层属性并将它们添加为子图层。
然后,在
layoutSubviews()
中,我们知道了视图的框架(如果框架发生变化,它将再次调用),我们可以定义路径和视图的角半径。
然后在“可设置”属性(颜色、速度等)中,我们只需要更新相关元素属性即可。
快速修改您的课程...
CircularProgressView类
class CircularProgressView: UIView {
var progressLayer = CAShapeLayer()
var trackLayer = CAShapeLayer()
var speedLabel = UILabel()
var progressColor = UIColor.white {
didSet {
progressLayer.strokeColor = progressColor.cgColor
}
}
var trackColor = UIColor.white {
didSet {
trackLayer.strokeColor = trackColor.cgColor
}
}
// assuming we are setting the speed to an Int value
// between 0 and 100
var speed: Int = 0 {
didSet {
// don't exceed 100
let spd: Int = min(speed, 100)
speedLabel.text = String(spd)
progressLayer.strokeEnd = CGFloat(spd) / 100.0
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
createCircularPath()
configureSpeedLabel()
}
override func layoutSubviews() {
super.layoutSubviews()
// this is where we know the frame of the view (self)
// so here we can define the corner radius and the path
self.layer.cornerRadius = self.frame.width/2
let path = UIBezierPath(arcCenter: CGPoint(x: frame.width/2, y: frame.height/2), radius: (frame.width - 1.5)/2, startAngle: CGFloat(-0.5 * .pi), endAngle: CGFloat(1.5 * .pi), clockwise: true)
trackLayer.path = path.cgPath
progressLayer.path = path.cgPath
}
fileprivate func configureSpeedLabel() {
// this all only needs to be done once -- on init
// the only thing that will change is the text
speedLabel.translatesAutoresizingMaskIntoConstraints = false
speedLabel.text = String(speed)
speedLabel.font = UIFont(name: "TimesNewRomanPSMT", size: 24)
speedLabel.textColor = UIColor.black
speedLabel.isOpaque = true
self.addSubview(speedLabel)
NSLayoutConstraint.activate([
speedLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor),
speedLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor)
])
}
fileprivate func createCircularPath() {
// this all only needs to be done once -- on init
self.backgroundColor = UIColor.clear
// set the initial properties
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.strokeColor = trackColor.cgColor
trackLayer.opacity = 0.3
trackLayer.lineWidth = 10.0
trackLayer.strokeEnd = 1.0
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = progressColor.cgColor
progressLayer.lineWidth = 10.0
progressLayer.strokeEnd = 0.0
// add these sublayers
self.layer.addSublayer(trackLayer)
self.layer.addSublayer(progressLayer)
}
}
控制器类示例
class CPVExampleViewController: UIViewController {
let cpv = CircularProgressView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
cpv.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(cpv)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
cpv.centerXAnchor.constraint(equalTo: g.centerXAnchor),
cpv.centerYAnchor.constraint(equalTo: g.centerYAnchor),
cpv.widthAnchor.constraint(equalToConstant: 280.0),
cpv.heightAnchor.constraint(equalTo: cpv.widthAnchor),
])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if cpv.speed >= 100 {
cpv.speed = 0
} else {
cpv.speed += 10
}
}
}
结果:
每次点击任意位置都会将
.speed
增加 10
——直到到达 100
,然后它将重置为 0