如何在 swift 中使用贝塞尔曲线路径创建不同大小的饼图?
How to create graph pies with different sizes using bézier paths in swift?
我想制作一个漂亮的图形饼图,它有 8 个相等的切片,可以根据 Int 或类似的东西单独缩放或调整大小。这看起来像下面这样,只是所有的切片都应该被平均切割:
我在 Objective-C 中尝试过这个,但它只做了一片:
-(CAShapeLayer *)createPieSlice {
CAShapeLayer *slice = [CAShapeLayer layer];
slice.fillColor = [UIColor redColor].CGColor;
slice.strokeColor = [UIColor blackColor].CGColor;
slice.lineWidth = 3.0;
CGFloat angle = DEG2RAD(-60.0);
CGPoint center = CGPointMake(100.0, 100.0);
CGFloat radius = 100.0;
UIBezierPath *piePath = [UIBezierPath bezierPath];
[piePath moveToPoint:center];
[piePath addLineToPoint:CGPointMake(center.x + radius * cosf(angle), center.y + radius * sinf(angle))];
[piePath addArcWithCenter:center radius:radius startAngle:angle endAngle:DEG2RAD(60.0) clockwise:YES];
// [piePath addLineToPoint:center];
[piePath closePath]; // this will automatically add a straight line to the center
slice.path = piePath.CGPath;
return slice;
}
如何在 swift 中实现该图表?
将问题分解成符合逻辑的部分。
您有不同弧宽的楔形。所有这些半径需要加起来是一个完整的圆。我假设它们代表加起来为 100% 的分数。您需要特定订单吗?如果是这样,请按照您想要的顺序映射您的分数,这样它们加起来就是 100%。
然后编写从零角度开始的代码,并创建 2π 的指定分数的弧。每一个都将从前一个的末尾开始。根据您需要的数据分配合适的半径。
现在编写在 UIBezierPath 中创建闭合路径段的代码。
编辑
您已经澄清并告诉我们您总是想要 8 个宽度相同但半径不同的切片。
因此您需要编写代码来获取 8 个输入值并将其绘制为 8 个具有不同半径值的圆弧。
假设您的输入值是一个范围从 0 到 1 的浮点数数组。在零处,楔形的大小为零。在 1.0 时,它是适合您视图的最大圆尺寸(正方形视图宽度的一半。
因此您将创建一个包含 8 个浮点数的数组:
var fractions = [0.5, 0.7, 0.3, 0.1, 1.0 .6, .2, .9]
创建具有 8 条弧的贝塞尔曲线的代码可能如下所示:
let pi = 3.1415826
let largestRadius = myView.width/2
let piePath = UIBezierPath()
for (index, afloat) in fractions
{
let startAngle = Double(index) / fractions.count * 2 * pi
let endAngle = Double(index+1) / fractions.count * 2 * pi
let thisRadius = largestRadius * afloat
let center = CGPointMake( myView.width/2, myView.height/2)
piePath.moveToPoint(center)
piePath.addArcWithCenter(center,
radius: thisRadius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true)
piePath.lineToPoint(center)
piePath.closePath()
}
我 认为 上面的代码会创建 8 个封闭的饼图切片路径,但我并不肯定。可能需要在第一个 moveToPoint 调用和 arc 调用之间添加一个 lineToPoint 调用。
编辑#2:
由于我正在学习 Swift,我决定将其作为练习并编写了一个示例项目,该项目使用形状层和从 UIBezierPath 创建的自定义路径生成饼图,如上所述。您可以在 github 上找到示例项目:PieCharts project on Github
我已经成功地使用 Core Graphics 解决了我的问题!感谢@duncan-c 的关注。
编辑:
我放弃了我的第一个解决方案,转而使用 @duncan-c 的解决方案,它更适合我的需要!
import UIKit
class Pie: UIView {
// In range of 0.0 to 1.0
var endArc:CGFloat = 0.0 {
didSet {
setNeedsDisplay()
}
}
var arcWidth:CGFloat = 5.0
var arcColor = UIColor()
var arcBackgroundColor = UIColor.clearColor()
var arcStrokeColor = UIColor()
var startFloat:CGFloat = 0.0
var radius:CGFloat = 0.0
var radiusSize: CGFloat = 0.0
override func drawRect(rect: CGRect) {
// Important constants for circle
let fullCircle = 2.0 * CGFloat(M_PI)
let start:CGFloat = startFloat * fullCircle
let end:CGFloat = endArc * fullCircle + start
// Find the centerpoint of the rect
var centerPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))
// Set the radius
radius = (radiusSize - arcWidth) / 2.0
// Starting point for all drawing code is getting the context.
let context = UIGraphicsGetCurrentContext()
// Set colorspace
let colorspace = CGColorSpaceCreateDeviceRGB()
// Set line attributes
CGContextSetLineWidth(context, arcWidth)
// Draw the pie
CGContextSetStrokeColorWithColor(context, arcStrokeColor.CGColor)
CGContextSetFillColorWithColor(context, arcColor.CGColor)
CGContextMoveToPoint(context, centerPoint.x, centerPoint.y)
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
CGContextFillPath(context)
}
}
然后在我需要使用的地方对 UIView 进行子类化:
@IBOutlet weak var graph: Pie!
override func awakeFromNib() {
super.awakeFromNib()
let backgroundTrackColor = UIColor.clearColor()
let strokeColor = UIColor(white: 0.15, alpha: 1.0)
graph.radiusSize = 50.0
graph.arcBackgroundColor = backgroundTrackColor
graph.arcStrokeColor = strokeColor
graph.arcColor = UIColor.redColor()
graph.startFloat = 0.750
graph.arcWidth = 5.0
graph.endArc = 0.125
}
我想制作一个漂亮的图形饼图,它有 8 个相等的切片,可以根据 Int 或类似的东西单独缩放或调整大小。这看起来像下面这样,只是所有的切片都应该被平均切割:
我在 Objective-C 中尝试过这个,但它只做了一片:
-(CAShapeLayer *)createPieSlice {
CAShapeLayer *slice = [CAShapeLayer layer];
slice.fillColor = [UIColor redColor].CGColor;
slice.strokeColor = [UIColor blackColor].CGColor;
slice.lineWidth = 3.0;
CGFloat angle = DEG2RAD(-60.0);
CGPoint center = CGPointMake(100.0, 100.0);
CGFloat radius = 100.0;
UIBezierPath *piePath = [UIBezierPath bezierPath];
[piePath moveToPoint:center];
[piePath addLineToPoint:CGPointMake(center.x + radius * cosf(angle), center.y + radius * sinf(angle))];
[piePath addArcWithCenter:center radius:radius startAngle:angle endAngle:DEG2RAD(60.0) clockwise:YES];
// [piePath addLineToPoint:center];
[piePath closePath]; // this will automatically add a straight line to the center
slice.path = piePath.CGPath;
return slice;
}
如何在 swift 中实现该图表?
将问题分解成符合逻辑的部分。
您有不同弧宽的楔形。所有这些半径需要加起来是一个完整的圆。我假设它们代表加起来为 100% 的分数。您需要特定订单吗?如果是这样,请按照您想要的顺序映射您的分数,这样它们加起来就是 100%。
然后编写从零角度开始的代码,并创建 2π 的指定分数的弧。每一个都将从前一个的末尾开始。根据您需要的数据分配合适的半径。
现在编写在 UIBezierPath 中创建闭合路径段的代码。
编辑
您已经澄清并告诉我们您总是想要 8 个宽度相同但半径不同的切片。
因此您需要编写代码来获取 8 个输入值并将其绘制为 8 个具有不同半径值的圆弧。
假设您的输入值是一个范围从 0 到 1 的浮点数数组。在零处,楔形的大小为零。在 1.0 时,它是适合您视图的最大圆尺寸(正方形视图宽度的一半。
因此您将创建一个包含 8 个浮点数的数组:
var fractions = [0.5, 0.7, 0.3, 0.1, 1.0 .6, .2, .9]
创建具有 8 条弧的贝塞尔曲线的代码可能如下所示:
let pi = 3.1415826
let largestRadius = myView.width/2
let piePath = UIBezierPath()
for (index, afloat) in fractions
{
let startAngle = Double(index) / fractions.count * 2 * pi
let endAngle = Double(index+1) / fractions.count * 2 * pi
let thisRadius = largestRadius * afloat
let center = CGPointMake( myView.width/2, myView.height/2)
piePath.moveToPoint(center)
piePath.addArcWithCenter(center,
radius: thisRadius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true)
piePath.lineToPoint(center)
piePath.closePath()
}
我 认为 上面的代码会创建 8 个封闭的饼图切片路径,但我并不肯定。可能需要在第一个 moveToPoint 调用和 arc 调用之间添加一个 lineToPoint 调用。
编辑#2:
由于我正在学习 Swift,我决定将其作为练习并编写了一个示例项目,该项目使用形状层和从 UIBezierPath 创建的自定义路径生成饼图,如上所述。您可以在 github 上找到示例项目:PieCharts project on Github
我已经成功地使用 Core Graphics 解决了我的问题!感谢@duncan-c 的关注。
编辑: 我放弃了我的第一个解决方案,转而使用 @duncan-c 的解决方案,它更适合我的需要!
import UIKit
class Pie: UIView {
// In range of 0.0 to 1.0
var endArc:CGFloat = 0.0 {
didSet {
setNeedsDisplay()
}
}
var arcWidth:CGFloat = 5.0
var arcColor = UIColor()
var arcBackgroundColor = UIColor.clearColor()
var arcStrokeColor = UIColor()
var startFloat:CGFloat = 0.0
var radius:CGFloat = 0.0
var radiusSize: CGFloat = 0.0
override func drawRect(rect: CGRect) {
// Important constants for circle
let fullCircle = 2.0 * CGFloat(M_PI)
let start:CGFloat = startFloat * fullCircle
let end:CGFloat = endArc * fullCircle + start
// Find the centerpoint of the rect
var centerPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))
// Set the radius
radius = (radiusSize - arcWidth) / 2.0
// Starting point for all drawing code is getting the context.
let context = UIGraphicsGetCurrentContext()
// Set colorspace
let colorspace = CGColorSpaceCreateDeviceRGB()
// Set line attributes
CGContextSetLineWidth(context, arcWidth)
// Draw the pie
CGContextSetStrokeColorWithColor(context, arcStrokeColor.CGColor)
CGContextSetFillColorWithColor(context, arcColor.CGColor)
CGContextMoveToPoint(context, centerPoint.x, centerPoint.y)
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, start, end, 0)
CGContextFillPath(context)
}
}
然后在我需要使用的地方对 UIView 进行子类化:
@IBOutlet weak var graph: Pie!
override func awakeFromNib() {
super.awakeFromNib()
let backgroundTrackColor = UIColor.clearColor()
let strokeColor = UIColor(white: 0.15, alpha: 1.0)
graph.radiusSize = 50.0
graph.arcBackgroundColor = backgroundTrackColor
graph.arcStrokeColor = strokeColor
graph.arcColor = UIColor.redColor()
graph.startFloat = 0.750
graph.arcWidth = 5.0
graph.endArc = 0.125
}