iOS Swift 圆形图表

Sea*_*ean -3 charts swift

对于如何为 iOS Swift 实现以下进度表有什么建议吗?

在此输入图像描述

Rob*_*Rob 5

只需将其分解为单独的步骤即可。

\n

第一个问题是如何绘制单独的刻度线。

\n

一种方法是使用 绘制四个笔画UIBezierPath

\n
    \n
  • 外半径处的顺时针圆弧;
  • \n
  • 一条到内半径的线;
  • \n
  • 内半径处的逆时针圆弧;和
  • \n
  • 一条回到外半径的线。
  • \n
\n

事实证明,您可以跳过这两行,只需添加这两个圆弧,然后关闭路径,您\xe2\x80\x99就完成了。将为UIBezierPath您添加两条弧线之间的线。例如:

\n
let startAngle: CGFloat = 2 * .pi * (CGFloat(i) - 0.2) / CGFloat(tickCount)\nlet endAngle: CGFloat = 2 * .pi * (CGFloat(i) + 0.2) / CGFloat(tickCount)\n\n// create path for individual tickmark\n\nlet path = UIBezierPath()\npath.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)\npath.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false)\npath.close()\n\n// use that path in a `CAShapeLayer`\n\nlet shapeLayer = CAShapeLayer()\nshapeLayer.fillColor = \xe2\x80\xa6\nshapeLayer.strokeColor = UIColor.clear.cgColor\nshapeLayer.path = path.cgPath\n\n// add it to our view\xe2\x80\x99s `layer`\n\nview.layer.addSublayer(shapeLayer)\n
Run Code Online (Sandbox Code Playgroud)\n

对和i之间重复此操作,其中是,并且您有 90 个刻度线:0tickCounttickCount90

\n

在此输入图像描述

\n

显然,可以使用您想要的任何颜色,将进度范围之外的颜色设置为灰色,等等。但希望这说明了如何使用UIBezierPath渲染两个弧并使用指定颜色填充每个刻度线的形状的基本思想。

\n
\n

例如:

\n
class CircularTickView: UIView {\n    var progress: CGFloat = 0.7 { didSet { setNeedsLayout() } }\n\n    private var shapeLayers: [CAShapeLayer] = []\n    private let startHue: CGFloat = 0.33\n    private let endHue: CGFloat = 0.66\n    private let outOfBoundsColor: UIColor = .lightGray\n    \n    override func layoutSubviews() {\n        super.layoutSubviews()\n        \n        shapeLayers.forEach { $0.removeFromSuperlayer() }\n        shapeLayers = []\n        \n        let outerRadius = min(bounds.width, bounds.height) / 2\n        let innerRadius = outerRadius * 0.7\n        let center = CGPoint(x: bounds.midX, y: bounds.midY)\n        let tickCount = 90\n        \n        for i in 0 ..< tickCount {\n            let shapeLayer = CAShapeLayer()\n            shapeLayer.fillColor = color(percent: CGFloat(i) / CGFloat(tickCount)).cgColor\n            shapeLayer.strokeColor = UIColor.clear.cgColor\n            \n            let startAngle: CGFloat = 2 * .pi * (CGFloat(i) - 0.2) / CGFloat(tickCount)\n            let endAngle: CGFloat = 2 * .pi * (CGFloat(i) + 0.2) / CGFloat(tickCount)\n            \n            let path = UIBezierPath()\n            path.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)\n            path.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false)\n            path.close()\n            shapeLayer.path = path.cgPath\n            \n            layer.addSublayer(shapeLayer)\n            shapeLayers.append(shapeLayer)\n        }\n    }\n\n    private func color(percent: CGFloat) -> UIColor {\n        if percent > progress {\n            return outOfBoundsColor\n        } \n        \n        let hue = (endHue - startHue) * percent + startHue\n        return UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

显然,您需要根据需要进行调整。也许改变颜色算法。也许让它从 12 o\xe2\x80\x99clock 开始,而不是 3 o\xe2\x80\x99clock。等等。细节并不重要,重要的是了解如何添加带有 UI 路径的形状图层的基本思想。

\n
\n

虽然我更喜欢使用形状图层,但您也可以使用覆盖 的再现draw(_:)

\n
class CircularTickView: UIView { \n    var progress: CGFloat = 0.7 { didSet { setNeedsDisplay() } }\n\n    private let startHue: CGFloat = 0.33\n    private let endHue: CGFloat = 0.66\n    private let outOfBoundsColor: UIColor = .lightGray\n    \n    override func draw(_ rect: CGRect) {\n        super.draw(rect)\n        \n        let outerRadius = min(bounds.width, bounds.height) / 2\n        let innerRadius = outerRadius * 0.7\n        let center = CGPoint(x: bounds.midX, y: bounds.midY)\n        let tickCount = 90\n        \n        for i in 0 ..< tickCount {\n            let startAngle: CGFloat = 2 * .pi * (CGFloat(i) - 0.2) / CGFloat(tickCount)\n            let endAngle: CGFloat = 2 * .pi * (CGFloat(i) + 0.2) / CGFloat(tickCount)\n            \n            let path = UIBezierPath()\n            path.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)\n            path.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false)\n            path.close()\n\n            if path.cgPath.intersects(UIBezierPath(rect: rect).cgPath)) {\n                color(percent: CGFloat(i) / CGFloat(tickCount))\n                    .setFill()\n                path.fill() \n            }\n        }\n    }\n\n    private func color(percent: CGFloat) -> UIColor {\n        if percent > progress {\n            return outOfBoundsColor\n        } \n        \n        let hue = (endHue - startHue) * percent + startHue\n        return UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n