如何解决我可以使用 UIBezier 绘制椭圆形进度条吗
如何快速绘制具有起始和结束角度的椭圆?
就像我使用的方法init(arcCenter:radius:startAngle:endAngle:clock:)
画一个有间隙的圆。
我尝试使用 init(ovalln:) 和 the relation of the bezier Curve and ellipse 绘制一个有间隙的椭圆。
然而,它最终只出现了一个完美的椭圆形。
如何绘制一个具有如下图所示间隙的椭圆?谢谢!
解决方法
可能适合您,也可能不适合您,一种方法是绘制一条留下间隙的弧线,然后在 y 轴上缩放路径。
弧从 3 点钟位置的零度(或弧度)开始。由于您的差距在顶部,我们可以通过将度数“平移”-90 来使事情变得更容易,因此我们可以根据 12 点钟的零度数来考虑......也就是说,如果我们想从20 度(零在顶部)并以 340 度结束,我们弧的起始角度是 (20 - 90),结束弧是 (340 - 90)。
所以,我们首先制作一个带有贝塞尔曲线的圆 - startAngle == 0,endAngle == 360:
接下来,我们将调整开始和结束角度,在顶部为我们提供 40 度的“间隙”:
然后我们可以缩放转换该路径,使其看起来像这样:
还有,如果没有“内部”线条,它会是什么样子:
然后,我们使用相同的圆弧半径、startAngle 和缩放比例覆盖另一条贝塞尔曲线,但我们将 endAngle 设置为完整圆弧的百分比。
在 40 度间隙的情况下,完整的弧度将为 (360 - 40)。
现在,我们将其作为“进度条”:
这是一个完整的例子:
class EllipseProgressView: UIView {
public var gapAngle: CGFloat = 40 {
didSet {
setNeedsLayout()
layoutIfNeeded()
}
}
public var progress: CGFloat = 0.0 {
didSet {
setNeedsLayout()
layoutIfNeeded()
}
}
public var baseColor: UIColor = .lightGray {
didSet {
ellipseBaseLayer.strokeColor = baseColor.cgColor
setNeedsLayout()
layoutIfNeeded()
}
}
public var progressColor: UIColor = .red {
didSet {
ellipseProgressLayer.strokeColor = progressColor.cgColor
setNeedsLayout()
layoutIfNeeded()
}
}
private let ellipseBaseLayer = CAShapeLayer()
private let ellipseProgressLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
backgroundColor = .black
layer.addSublayer(ellipseBaseLayer)
layer.addSublayer(ellipseProgressLayer)
ellipseBaseLayer.lineWidth = 3.0
ellipseBaseLayer.fillColor = UIColor.clear.cgColor
ellipseBaseLayer.strokeColor = baseColor.cgColor
ellipseBaseLayer.lineCap = .round
ellipseProgressLayer.lineWidth = 5.0
ellipseProgressLayer.fillColor = UIColor.clear.cgColor
ellipseProgressLayer.strokeColor = progressColor.cgColor
ellipseProgressLayer.lineCap = .round
}
override func layoutSubviews() {
var startAngle: CGFloat = 0
var endAngle: CGFloat = 0
var startRadians: CGFloat = 0
var endRadians: CGFloat = 0
var pth: UIBezierPath!
startAngle = gapAngle * 0.5
endAngle = 360 - gapAngle * 0.5
// totalAngle is (360-degrees minus the gapAngle)
let totalAngle: CGFloat = 360 - gapAngle
let center = CGPoint(x: bounds.midX,y: bounds.midY)
let radius = bounds.width * 0.5
let yScale: CGFloat = bounds.height / bounds.width
let origHeight = radius * 2.0
let ovalHeight = origHeight * yScale
let y = (origHeight - ovalHeight) * 0.5
// degrees start with Zero at 3 o'clock,so
// translate them to start at 12 o'clock
startRadians = (startAngle - 90).degreesToRadians
endRadians = (endAngle - 90).degreesToRadians
// new bezier path
pth = UIBezierPath()
// arc with "gap" at the top
pth.addArc(withCenter: center,radius: radius,startAngle: startRadians,endAngle: endRadians,clockwise: true)
// translate on the y-axis
pth.apply(CGAffineTransform(translationX: 0.0,y: y))
// scale the y-axis
pth.apply(CGAffineTransform(scaleX: 1.0,y: yScale))
ellipseBaseLayer.path = pth.cgPath
// new endAngle is startAngle plus the percentage of the total angle
endAngle = startAngle + totalAngle * progress
// degrees start with Zero at 3 o'clock,so
// translate them to start at 12 o'clock
startRadians = (startAngle - 90).degreesToRadians
endRadians = (endAngle - 90).degreesToRadians
// new bezier path
pth = UIBezierPath()
pth.addArc(withCenter: center,clockwise: true)
// translate on the y-axis
pth.apply(CGAffineTransform(translationX: 0.0,y: yScale))
ellipseProgressLayer.path = pth.cgPath
}
}
还有一个可以尝试的示例视图控制器——每次点击都会使“进度”增加 5%,直到达到 100%,然后我们从零开始:
class EllipseVC: UIViewController {
var progress: CGFloat = 0.0
let ellipseProgressView = EllipseProgressView()
let percentLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
ellipseProgressView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(ellipseProgressView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain 300-pts wide
ellipseProgressView.widthAnchor.constraint(equalToConstant: 300.0),// height is 1 / 3rd of width
ellipseProgressView.heightAnchor.constraint(equalTo: ellipseProgressView.widthAnchor,multiplier: 1.0 / 3.0),// center in view safe area
ellipseProgressView.centerXAnchor.constraint(equalTo: g.centerXAnchor),ellipseProgressView.centerYAnchor.constraint(equalTo: g.centerYAnchor),])
// base line color is lightGray
// progress line color is red
// we can change those,if desired
// for example:
//ellipseProgressView.baseColor = .green
//ellipseProgressView.progressColor = .yellow
// "gap" angle default is 40-degrees
// we can change that,if desired
// for example:
//ellipseProgressView.gapAngle = 40
// add a label to show the current progress
percentLabel.translatesAutoresizingMaskIntoConstraints = false
percentLabel.textColor = .white
view.addSubview(percentLabel)
NSLayoutConstraint.activate([
percentLabel.topAnchor.constraint(equalTo: ellipseProgressView.bottomAnchor,constant: 8.0),percentLabel.centerXAnchor.constraint(equalTo: ellipseProgressView.centerXAnchor),])
updatePercentLabel()
}
override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) {
// increment progress by 5% on each tap
// reset to Zero when we get past 100%
progress += 5
if progress.rounded() > 100.0 {
progress = 0.0
}
ellipseProgressView.progress = (progress / 100.0)
updatePercentLabel()
}
func updatePercentLabel() -> Void {
percentLabel.text = String(format: "%0.2f %%",progress)
}
}
请注意:这是示例代码!!!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。