我正在尝试制作一个饼图。实际上它已经完成了,但是我想得到一些值,每个值应该是馅饼的一部分。我唯一能做的就是用滑块填充馅饼。如何为某些值制作不同颜色的不同切片?
这是我绘制图表的代码(我在这里堆叠):
import UIKit
@IBDesignable class ChartView: UIView {
@IBInspectable var progress : Double = 0.0 {
didSet {
self.setNeedsDisplay()
}
}
@IBInspectable var noProgress : Double = 0.0 {
didSet {
self.setNeedsDisplay()
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
self.contentMode = .Redraw
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clearColor()
self.contentMode = .Redraw
}
override func drawRect(rect: CGRect) {
let color = UIColor.blueColor().CGColor
let lineWidth : CGFloat = 2.0
// Calculate box with insets
let margin: CGFloat = lineWidth
let box0 = CGRectInset(self.bounds, margin, margin)
let side : CGFloat = min(box0.width, box0.height)
let box = CGRectMake((self.bounds.width-side)/2, (self.bounds.height-side)/2,side,side)
let ctx = UIGraphicsGetCurrentContext()
// Draw outline
CGContextBeginPath(ctx)
CGContextSetStrokeColorWithColor(ctx, UIColor.blackColor().CGColor)
CGContextSetLineWidth(ctx, lineWidth)
CGContextAddEllipseInRect(ctx, box)
CGContextClosePath(ctx)
CGContextStrokePath(ctx)
// Draw arc
let delta : CGFloat = -CGFloat(M_PI_2)
let radius : CGFloat = min(box.width, box.height)/2.0
func prog_to_rad(p: Double) -> CGFloat {
let rad = CGFloat((p * M_PI)/180)
return rad
}
func draw_arc(s: CGFloat, e: CGFloat, color: CGColor) {
CGContextBeginPath(ctx)
CGContextMoveToPoint(ctx, box.midX, box.midY)
CGContextSetFillColorWithColor(ctx, color)
CGContextAddArc(ctx, box.midX, box.midY, radius-lineWidth/2, s, e, 0)
CGContextClosePath(ctx)
CGContextFillPath(ctx)
}
if progress > 0 {
let s = prog_to_rad(noProgress * 360/100)
let e = prog_to_rad(progress * 360/100)
draw_arc(s, e, color)
}
}
}
这是我的ViewController:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var pieChartView: ChartView!
@IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func setValue(sender: UISlider) {
pieChartView.progress = Double(sender.value)
}
}
答案 0 :(得分:3)
此代码来自我的blogpost,它使用CAShapeLayer和UIBezierPath。您可以使用您喜欢的任何颜色选择创建任意数量的片段。
extension CGFloat {
func radians() -> CGFloat {
let b = CGFloat(M_PI) * (self/180)
return b
}
}
extension UIBezierPath {
convenience init(circleSegmentCenter center:CGPoint, radius:CGFloat, startAngle:CGFloat, endAngle:CGFloat)
{
self.init()
self.moveToPoint(CGPointMake(center.x, center.y))
self.addArcWithCenter(center, radius:radius, startAngle:startAngle.radians(), endAngle: endAngle.radians(), clockwise:true)
self.closePath()
}
}
func pieChart(pieces:[(UIBezierPath, UIColor)], viewRect:CGRect) -> UIView {
var layers = [CAShapeLayer]()
for p in pieces {
let layer = CAShapeLayer()
layer.path = p.0.CGPath
layer.fillColor = p.1.CGColor
layer.strokeColor = UIColor.whiteColor().CGColor
layers.append(layer)
}
let view = UIView(frame: viewRect)
for l in layers {
view.layer.addSublayer(l)
}
return view
}
let rectSize = CGRectMake(0,0,400,400)
let centrePointOfChart = CGPointMake(CGRectGetMidX(rectSize),CGRectGetMidY(rectSize))
let radius:CGFloat = 100
let piePieces = [(UIBezierPath(circleSegmentCenter: centrePointOfChart, radius: radius, startAngle: 250, endAngle: 360),UIColor.brownColor()), (UIBezierPath(circleSegmentCenter: centrePointOfChart, radius: radius, startAngle: 0, endAngle: 200),UIColor.orangeColor()), (UIBezierPath(circleSegmentCenter: centrePointOfChart, radius: radius, startAngle: 200, endAngle: 250),UIColor.lightGrayColor())]
pieChart(piePieces, viewRect: CGRectMake(0,0,400,400))
答案 1 :(得分:2)
您发布了一堆代码,似乎在单个颜色中绘制单个饼图“切片”。
你是说你不知道如何用不同大小的切片绘制整个馅饼,而你不知道如何使每个切片成为不同的颜色?
对我而言,就像你复制/粘贴你从某个地方获得的代码并且不知道它是如何工作的。你告诉我们你的代码做了什么,让我们更清楚地知道你被困在哪里?
我们不会在此处提取您的复制/粘贴代码并对其进行修改以使其符合您的要求。对我来说听起来像是定制开发。我不知道这块板子上的其他海报,但我得到了报酬。
碰巧我写了一篇开发博客文章,其中包括一个在Swift中生成饼图的示例应用程序。你可以在这里看到它:
http://wareto.com/swift-piecharts
不是像你发布的代码一样覆盖drawRect,而是创建一个包含饼图的CAShapeLayer。它管理具有可变数量“切片”的饼图,并将更改每个切片的弧度,半径或两者。
未将每个切片设置为不同的颜色。为此,您必须修改它以为每个切片使用单独的形状图层,这对程序来说是一个相当大的结构变化。
它至少向您展示了如何在Swift for iOS中绘制饼图:
答案 2 :(得分:0)
以下代码对于快速饼图切片空间很有用。结帐一次
导入UIKit
私有扩展CGFloat {
/// Formats the CGFloat to a maximum of 1 decimal place.
var formattedToOneDecimalPlace : String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 1
return formatter.string(from: NSNumber(value: self.native)) ?? "\(self)"
}
}
///定义饼图的一部分
结构段{
/// The color of the segment
var color : UIColor
/// The name of the segment
var name : String
/// The value of the segment
var value : CGFloat
}
PieChartView类:UIView {
/// An array of structs representing the segments of the pie chart
var segments = [Segment]() {
didSet {
totalValue = segments.reduce(0) { $0 + $1.value }
setupLabels()
setNeedsDisplay() // re-draw view when the values get set
layoutLabels();
} // re-draw view when the values get set
}
/// Defines whether the segment labels should be shown when drawing the pie chart
var showSegmentLabels = true {
didSet { setNeedsDisplay() }
}
/// Defines whether the segment labels will show the value of the segment in brackets
var showSegmentValueInLabel = false {
didSet { setNeedsDisplay() }
}
/// The font to be used on the segment labels
var segmentLabelFont = UIFont.systemFont(ofSize: 14) {
didSet {
textAttributes[NSAttributedStringKey.font] = segmentLabelFont
setNeedsDisplay()
}
}
private let paragraphStyle : NSParagraphStyle = {
var p = NSMutableParagraphStyle()
p.alignment = .center
return p.copy() as! NSParagraphStyle
}()
private lazy var textAttributes : [NSAttributedStringKey : NSObject] = {
return [NSAttributedStringKey.paragraphStyle : self.paragraphStyle, NSAttributedStringKey.font : self.segmentLabelFont]
}()
override init(frame: CGRect) {
super.init(frame: frame)
isOpaque = false // when overriding drawRect, you must specify this to maintain transparency.
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private var labels: [UILabel] = []
private var totalValue: CGFloat = 1;
override func draw(_ rect: CGRect) {
let anglePI2 = (CGFloat.pi * 2)
let center = CGPoint.init(x: bounds.size.width / 2, y: bounds.size.height / 2)
let radius = min(bounds.size.width, bounds.size.height) / 2;
let lineWidth: CGFloat = 1.5;
let ctx = UIGraphicsGetCurrentContext()
ctx?.setLineWidth(lineWidth)
var currentAngle: CGFloat = 0
if totalValue <= 0 {
totalValue = 1
}
let iRange = 0 ..< segments.count
for i in iRange {
let segment = segments[i]
// calculate percent
let percent = segment.value / totalValue
let angle = anglePI2 * percent
ctx?.beginPath()
ctx?.move(to: center)
ctx?.addArc(center: center, radius: radius - lineWidth, startAngle: currentAngle, endAngle: currentAngle + angle, clockwise: false)
ctx?.closePath()
ctx?.setFillColor(segment.color.cgColor)
ctx?.fillPath()
ctx?.beginPath()
ctx?.move(to: center)
ctx?.addArc(center: center, radius: radius - (lineWidth / 2), startAngle: currentAngle, endAngle: currentAngle + angle, clockwise: false)
ctx?.closePath()
ctx?.setStrokeColor(UIColor.white.cgColor)
ctx?.strokePath()
currentAngle += angle
}
}
override func layoutSubviews() {
super.layoutSubviews()
self.layoutLabels()
}
private func setupLabels() {
var diff = segments.count - labels.count;
if diff >= 0 {
for _ in 0 ..< diff {
let lbl = UILabel()
self.addSubview(lbl)
labels.append(lbl)
}
} else {
while diff != 0 {
var lbl: UILabel!
if labels.count <= 0 {
break;
}
lbl = labels.removeLast()
if lbl.superview != nil {
lbl.removeFromSuperview()
}
diff += 1;
}
}
for i in 0 ..< segments.count {
let lbl = labels[i]
lbl.textColor = UIColor.white
// Change here for your text display
// I currently display percent of each pies
lbl.text = "\(segments[i].value.formattedToOneDecimalPlace)%" //String.init(format: "%0.0f", segments[i].value)
lbl.font = UIFont.systemFont(ofSize: 14)
}
}
func layoutLabels() {
let anglePI2 = CGFloat.pi * 2
let center = CGPoint.init(x: bounds.size.width / 2, y: bounds.size.height / 2)
let radius = min(bounds.size.width / 2, bounds.size.height / 2) / 1.5
var currentAngle: CGFloat = 0;
let iRange = 0 ..< labels.count
for i in iRange {
let lbl = labels[i]
let percent = segments[i].value / totalValue
let intervalAngle = anglePI2 * percent;
lbl.frame = .zero;
lbl.sizeToFit()
let x = center.x + radius * cos(currentAngle + (intervalAngle / 2))
let y = center.y + radius * sin(currentAngle + (intervalAngle / 2))
lbl.center = CGPoint.init(x: x, y: y)
currentAngle += intervalAngle
}
}
}