Creating a Pie Chart in Swift

Pie chart using Charts library with swift

The reason all the information isn't showing up is because you are using a parent initialiser when creating the entry point.

Instead of

let dataEntry1 = ChartDataEntry(x: Double(i), y: values[i], data: dataPoints[i] as AnyObject)

try this instead

let dataEntry1 = PieChartDataEntry(value: Double(i), label: dataPoints[i], data:  dataPoints[i] as AnyObject)

The PieChartDataEntry is specifically for Pie charts so you should see the month show up in the chart.

Hopefully this gets you on the right track

Creating a pie chart in swift

You could create a convenience init that includes the data, but that would only be useful if you are creating the view from code. If your view is added in the Storyboard, you will want a way to set the data after the view has been created.

It is good to look at the standard UI elements (like UIButton) for design clues. You can change properties on a UIButton and it updates without you having to call myButton.setNeedsDisplay(), so you should design your pie chart to work in the same manner.

It is fine to have a property of your view that holds the data. The view should take responsibility for redrawing itself, so define didSet for your data property and call setNeedsDisplay() there.

var data:  Dictionary<String,Int>? {
didSet {
// Data changed. Redraw the view.
self.setNeedsDisplay()
}
}

Then you can simply set the data, and the pie chart will redraw:

pieChart.data = pieData

You can extend this to other properties on your pie chart. For instance, you might want to change the background color. You'd define didSet for that property as well and call setNeedsDisplay.

Note that setNeedsDisplay just sets a flag and the view will be drawn later. Multiple calls to setNeedsDisplay won't cause your view to redraw multiple times, so you can do something like:

pieChart.data = pieData
pieChart.backgroundColor = .redColor()
pieChart.draw3D = true // draw the pie chart in 3D

and the pieChart would redraw just once.

Making a pie chart using Core Graphics

You'll want to use the CGContextAddArc() function (CGContext.addArc() in Swift 3). This will let you create multiple segments for your pie chart by drawing an arc for each segment of your pie chart.

Something like this should do the trick:

import UIKit

struct Segment {

// the color of a given segment
var color: UIColor

// the value of a given segment – will be used to automatically calculate a ratio
var value: CGFloat
}

class PieChartView: UIView {

/// An array of structs representing the segments of the pie chart
var segments = [Segment]() {
didSet {
setNeedsDisplay() // re-draw view when the values get set
}
}

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)
}

override func draw(_ rect: CGRect) {

// get current context
let ctx = UIGraphicsGetCurrentContext()

// radius is the half the frame's width or height (whichever is smallest)
let radius = min(frame.size.width, frame.size.height) * 0.5

// center of the view
let viewCenter = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5)

// enumerate the total value of the segments by using reduce to sum them
let valueCount = segments.reduce(0, {$0 + $1.value})

// the starting angle is -90 degrees (top of the circle, as the context is flipped). By default, 0 is the right hand side of the circle, with the positive angle being in an anti-clockwise direction (same as a unit circle in maths).
var startAngle = -CGFloat.pi * 0.5

for segment in segments { // loop through the values array

// set fill color to the segment color
ctx?.setFillColor(segment.color.cgColor)

// update the end angle of the segment
let endAngle = startAngle + 2 * .pi * (segment.value / valueCount)

// move to the center of the pie chart
ctx?.move(to: viewCenter)

// add arc from the center for each segment (anticlockwise is specified for the arc, but as the view flips the context, it will produce a clockwise arc)
ctx?.addArc(center: viewCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)

// fill segment
ctx?.fillPath()

// update starting angle of the next segment to the ending angle of this segment
startAngle = endAngle
}
}
}

You can input your pie chart data as an array of Segment structs, where each Segment represents the color and value of that segment.

The value can be any float, and will automatically be reduced down to a ratio to be used in the pie chart. So for example, if you want your pie chart to represent the number of unsatisfied vs. number of satisfied customers, you can just pass the values directly in.

Example of usage:

let pieChartView = PieChartView()
pieChartView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 400)
pieChartView.segments = [
Segment(color: .red, value: 57),
Segment(color: .blue, value: 30),
Segment(color: .green, value: 25),
Segment(color: .yellow, value: 40)
]
view.addSubview(pieChartView)

Output:

Sample Image


Full project (with some extra functionality): https://github.com/hamishknight/Pie-Chart-View

how to create the pie Chart and fill percentage in swift

I think that code snippet was taken from https://stackoverflow.com/a/33947052/1271826 where I imagined a simple PieView class defined something like:

import UIKit

public class DataPoint {
let text: String
let value: Float
let color: UIColor

public init(text: String, value: Float, color: UIColor) {
self.text = text
self.value = value
self.color = color
}
}

@IBDesignable public class PieView: UIView {

public var dataPoints: [DataPoint]? { // use whatever type that makes sense for your app, though I'd suggest an array (which is ordered) rather than a dictionary (which isn't)
didSet { setNeedsDisplay() }
}

@IBInspectable public var lineWidth: CGFloat = 2 {
didSet { setNeedsDisplay() }
}

override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
dataPoints = [
DataPoint(text: "foo", value: 3, color: UIColor.redColor()),
DataPoint(text: "bar", value: 4, color: UIColor.yellowColor()),
DataPoint(text: "baz", value: 5, color: UIColor.blueColor())
]
}

public override func drawRect(rect: CGRect) {
guard dataPoints != nil else {
return
}

let center = CGPoint(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0)
let radius = min(bounds.size.width, bounds.size.height) / 2.0 - lineWidth
let total = dataPoints?.reduce(Float(0)) { $0 + $1.value }
var startAngle = CGFloat(-M_PI_2)

UIColor.blackColor().setStroke()

for dataPoint in dataPoints! {
let endAngle = startAngle + CGFloat(2.0 * M_PI) * CGFloat(dataPoint.value / total!)

let path = UIBezierPath()
path.moveToPoint(center)
path.addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.closePath()
path.lineWidth = lineWidth
dataPoint.color.setFill()

path.fill()
path.stroke()

startAngle = endAngle
}
}

public override func layoutSubviews() {
super.layoutSubviews()
setNeedsDisplay()
}
}

And, if you added a UIView in IB and changed the base class to PieView and add an @IBOutlet, you could then do something like:

class ViewController: UIViewController {

@IBOutlet weak var pieView: PieView!

override func viewDidLoad() {
super.viewDidLoad()

pieView.dataPoints = [
DataPoint(text: "foo", value: 3, color: UIColor.redColor()),
DataPoint(text: "bar", value: 4, color: UIColor.yellowColor()),
DataPoint(text: "baz", value: 5, color: UIColor.blueColor())
]
}

}

That yields:

Sample Image

Note, that's @IBDesignable (and implements prepareForInterfaceBuilder) so that when you add it to a storyboard, you'll see a sample pie chart.

Clearly, this is an absurdly simplistic implementation, but you can expand upon this as you see fit.

PieChart using Charts on swift. Need to remove the values of the elements

You need to set the text color as clear, it will work fine. Check out following change in your current code

let pieChartData = PieChartData(dataSet: pieChartDataSet)
pieChartData.setValueTextColor(NSUIColor.clear)
pieChart.data = pieChartData

How can I create a Pie Chart with rounded slice edges?

Solved it, and here's the output:

ring-chart-img

Basically I am using this library, called CircularProgressView (https://cocoapods.org/pods/CircleProgressView) to achieve the individual rings. Since I needed 5 rings, I stacked 5 such views (with clear background) on top of each other and rotated them to achieve my desired effect.

Cocoapod setup

First, you have to install the CircularProgressView pod in your project. I am using Cocoapods for importing the library here.

NOTE: If you do not have Cocoapods setup already then you need to install Cocoapods first using steps here: https://guides.cocoapods.org/using/getting-started.html

To add it using Cocoapods, add the following line in your Podfile.

pod 'CircleProgressView'

and run pod install on the Terminal from within your project directory.

Interface Builder

In your Storyboard file, go to your View Controller and add a view and set its contents. You can also create this view programmatically but here I will be using the storyboard.

Create 4 more views and use Auto Layout to stack them on top of one another.

Select all 5 views and go to the Identity Inspector (right panel in storyboard, 4th item in top bar).

Set the Class field value (under Custom Class) as 'CircularProgressView'.

Link all 5 views to your ViewController.swift file.

I have named them as follows:

@IBOutlet weak var pieChart1: CircleProgressView!
@IBOutlet weak var pieChart2: CircleProgressView!
@IBOutlet weak var pieChart3: CircleProgressView!
@IBOutlet weak var pieChart4: CircleProgressView!
@IBOutlet weak var pieChart5: CircleProgressView!

Call the function showRingChart() in viewDidLoad() to setup the views.

Here is the code for the functions:

// Function to convert degrees to radian
func degToRad(_ rotationDegrees: Double) -> CGFloat {
let rotationAngle = CGFloat(rotationDegrees * .pi/180.0)
return rotationAngle
}

// Function to Show Ring Chart
func showRingChart() {

// Values for the graph, can be changed as per your need
let val1 = self.val1
let val2 = self.val2
let val3 = self.val3
let val4 = self.val4
let val5 = self.val5
let totalVal = (val1 + val2 + val3 + val4 + val5)
var spacing = 0.05 * totalVal // Spacing is set to 5% (ie. 0.05). Change it according to your needs

print("Spacing: ", spacing)
let totalSpacing = 5 * spacing //Total spacing value to be added in the chart

let total = totalVal + totalSpacing //Total corresponding to 100% on the chart

if val1 == 0.0
&& val2 == 0.0
&& val3 == 0.0
&& val4 == 0.0
&& val5 == 0.0 {

// NO DATA, HIDE ALL CHARTS
pieChart1.isHidden = true
pieChart2.isHidden = true
pieChart3.isHidden = true
pieChart4.isHidden = true
pieChart5.isHidden = true

} else {

// DATA AVAILABLE

// Calculate Percentage of each value in the ring chart (ie. progress)
let valOnePerc = (val1 / total)
let valTwoPerc = (val2 / total)
let valThreePerc = (val3 / total)
let valFourPerc = (val4 / total)
let valFivePerc = (val5 / total)

let spacingPerc = spacing / total

// Angle offsets (in degrees)
let offset1 = (valOnePerc + spacingPerc) * 360
let offset2 = (valOnePerc + valTwoPerc + (2 * spacingPerc)) * 360
let offset3 = (valOnePerc + valTwoPerc + valThreePerc + (3 * spacingPerc)) * 360
let offset4 = (valOnePerc + valTwoPerc + valThreePerc + valFourPerc + (4 * spacingPerc)) * 360

print("--- PRINTING CHART VALUES HERE ---- ")
print(total)
print(valOnePerc)
print(valTwoPerc)
print(valThreePerc)
print(valFourPerc)
print(valFivePerc)
print(offset1)
print(offset2)
print(offset3)
print(offset4)
print("------------------")

// Setup ring chart sections
pieChart1.trackFillColor = UIColor.tintColorOne
pieChart1.progress = valOnePerc

pieChart2.trackFillColor = UIColor.tintColorTwo
pieChart2.progress = valTwoPerc
pieChart2.transform = CGAffineTransform(rotationAngle: degToRad(offset1))

pieChart3.trackFillColor = UIColor.tintColorThree
pieChart3.progress = valThreePerc
pieChart3.transform = CGAffineTransform(rotationAngle: degToRad(offset2))

pieChart4.trackFillColor = UIColor.tintColorFour
pieChart4.progress = valFourPerc
pieChart4.transform = CGAffineTransform(rotationAngle: degToRad(offset3))

pieChart5.trackFillColor = UIColor.tintColorFive
pieChart5.progress = valFivePerc
pieChart5.transform = CGAffineTransform(rotationAngle: degToRad(offset4))

// Unhide all charts
pieChart1.isHidden = false
pieChart2.isHidden = false
pieChart3.isHidden = false
pieChart4.isHidden = false
pieChart5.isHidden = false

}
}

It should do it. Change the (val1, val2, ..., val5) values to change the progress of the rings.

How to set specific color to each slice of Pie Chart?

You have a random color for your slices because you set a random color below for each dataPoint

for _ in 0..<dataPoints.count {

let red = Double(arc4random_uniform(256))
let green = Double(arc4random_uniform(256))
let blue = Double(arc4random_uniform(256))

let color = UIColor(red: CGFloat(red/255), green: CGFloat(green/255), blue: CGFloat(blue/255), alpha: 1)
colors.append(color)
}

If you need a specific color, then you need to set a specific color to the colors respective to each dataPoint. For an example if you have five data points, you need to have a colors array like below, so every time the slice for first dataPoint would be red, slice for second dataPoint is orange etc.

let colors: [UIColor] = [.red, .orange, .blue, .gray, .purple]
pieChartDataSet.colors = colors

Sample Image

How do I change the color of the separator between two slices of a pie chart in charts for iOS?

Its slice spacing between each slice of Pie Chart. If you want to change or increase that space then you can set space using yourPieChartDataSet.sliceSpace = 5.



Related Topics



Leave a reply



Submit