├── .gitignore ├── Package.swift ├── README.md ├── Sources └── VennPieChart │ └── VennPieChart.swift └── VennPiechart.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "VennPieChart", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "VennPieChart", 12 | targets: ["VennPieChart"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 21 | .target( 22 | name: "VennPieChart", 23 | dependencies: []) 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VennPieChart 2 | 3 | Venn Pie chart with tilt texts 4 | 5 | ## Screenshot 6 | 7 | ![alt text](https://github.com/NilaakashSingh/VennPieChart/blob/master/VennPiechart.png) 8 | 9 | ## How to use 10 | Updating soon ... 11 | -------------------------------------------------------------------------------- /Sources/VennPieChart/VennPieChart.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class VennPieChartView: UIView { 4 | 5 | public var positiveNumbers = 0 6 | public var positiveColor = UIColor.green 7 | public var negativeNumbers = 0 8 | public var negativeColor = UIColor.red 9 | 10 | @IBInspectable public var numberOfPositiveValues: Int { 11 | get { 12 | return positiveNumbers 13 | } 14 | set { 15 | positiveNumbers = newValue 16 | } 17 | } 18 | 19 | @IBInspectable public var positiveArcColor: UIColor? { 20 | get { 21 | return UIColor(cgColor: positiveColor.cgColor) 22 | } 23 | set { 24 | if let newValue = newValue { 25 | positiveColor = newValue 26 | } 27 | } 28 | } 29 | 30 | @IBInspectable public var numberOfNegativeValues: Int { 31 | get { 32 | return negativeNumbers 33 | } 34 | set { 35 | negativeNumbers = newValue 36 | } 37 | } 38 | @IBInspectable public var negativeArcColor: UIColor? { 39 | get { 40 | return UIColor(cgColor: negativeColor.cgColor) 41 | } 42 | set { 43 | if let newValue = newValue { 44 | negativeColor = newValue 45 | } 46 | } 47 | } 48 | 49 | override init(frame: CGRect) { 50 | super.init(frame: frame) 51 | } 52 | 53 | required init?(coder aDecoder: NSCoder) { 54 | super.init(coder: aDecoder) 55 | } 56 | 57 | public override func awakeFromNib() { 58 | super.awakeFromNib() 59 | DispatchQueue.main.async { 60 | self.drawArcWithParams(plottingView: self, count: self.positiveNumbers-1, isClockWise: true) 61 | self.drawArcWithParams(plottingView: self, count: self.negativeNumbers-1, isClockWise: false) 62 | } 63 | } 64 | 65 | /// Drawing number Arc in given View clockwise or anticlockwise 66 | private func drawArcWithParams(plottingView: UIView, count: Int, isClockWise: Bool) { 67 | var dynamicRadious:CGFloat = 0.0 68 | var value = 9 69 | var greenValue = 105.0 70 | var redValue = 30.0 71 | var blueValue = 57.0 72 | var arrLayers = [CAShapeLayer]() 73 | 74 | for _ in 0...count { 75 | //Drawing unfilled Arcs 76 | let center = CGPoint (x: plottingView.frame.size.width / 2, 77 | y: plottingView.frame.size.height / 2) 78 | 79 | let circleRadius = 60 + dynamicRadious + (((plottingView.frame.size.width / 2) - 60) / CGFloat(count + 1) / 2) 80 | 81 | let circlePathEmpty = UIBezierPath(arcCenter: center, radius: circleRadius, 82 | startAngle: CGFloat(Double.pi), 83 | endAngle: .zero, 84 | clockwise: isClockWise) 85 | 86 | let semiCircleLayerEmpty = CAShapeLayer() 87 | semiCircleLayerEmpty.path = circlePathEmpty.cgPath 88 | blueValue = blueValue - 3 89 | semiCircleLayerEmpty.strokeColor = UIColor.init(red: 34.0/255.0, green: CGFloat(blueValue/255.0), blue: 71.0/255.0, alpha: 1.0).cgColor 90 | semiCircleLayerEmpty.fillColor = UIColor.clear.cgColor 91 | semiCircleLayerEmpty.lineWidth = ((plottingView.frame.size.width / 2) - 60) / CGFloat(count + 1) 92 | semiCircleLayerEmpty.strokeStart = 0 93 | semiCircleLayerEmpty.strokeEnd = 1 94 | plottingView.layer.addSublayer(semiCircleLayerEmpty) 95 | 96 | 97 | // Drawing Filled Arcs with given value 98 | value -= 1 99 | let circlePath = UIBezierPath(arcCenter: center, radius: circleRadius, startAngle:CGFloat(Double.pi), endAngle: isClockWise ? CGFloat(-((Double(value))/3.0)) : CGFloat(((Double(value))/3.0)), clockwise: isClockWise) 100 | 101 | let semiCircleLayer = CAShapeLayer() 102 | semiCircleLayer.path = circlePath.cgPath 103 | greenValue = greenValue + 15.0 104 | redValue = redValue + 25.0 105 | semiCircleLayer.strokeColor = isClockWise ? UIColor(red: 48.0/255.0, green: CGFloat(greenValue/255.0), blue: 83.0/255.0, alpha: 1.0).cgColor : UIColor(red: CGFloat(redValue/255.0), green: 35.0/255.0, blue: 25.0/255.0, alpha: 1.0).cgColor 106 | semiCircleLayer.fillColor = UIColor.clear.cgColor 107 | semiCircleLayer.lineWidth = ((plottingView.frame.size.width / 2) - 60) / CGFloat(count + 1) //12 108 | dynamicRadious = dynamicRadious + ((plottingView.frame.size.width / 2) - 60) / CGFloat(count + 1) //12 109 | semiCircleLayer.strokeStart = 0 110 | semiCircleLayer.strokeEnd = 1 111 | 112 | // Saving layers in array to show text value 113 | arrLayers.append(semiCircleLayer) 114 | plottingView.layer.addSublayer(semiCircleLayer) 115 | } 116 | 117 | // Writing circular text along with Drawn Arc 118 | var i = 0 119 | dynamicRadious = 0.0 120 | for layer in arrLayers{ 121 | let circleRadius = 60 + dynamicRadious + (((plottingView.frame.size.height / 2) - 60) / CGFloat(count + 1) / 2) 122 | drawCurvedString(on: layer, text: NSAttributedString( 123 | string: isClockWise ? "Clockwise Text \(i)" : "Anticlockwise Text \(i)", 124 | attributes: [ 125 | NSAttributedString.Key.foregroundColor: UIColor.white, 126 | NSAttributedString.Key.font: UIFont.systemFont(ofSize: (((plottingView.frame.size.height / 2) - 60) / CGFloat(count + 1)/2)) 127 | ]), angle: isClockWise ? CGFloat(-((Double(8))/3.0 * 2.5) + 0.50) : CGFloat(-((Double(83.5))/3.0 * 2.5) + 0.50), radius: circleRadius, plottingViwe: plottingView, isClockwise: isClockWise) 128 | dynamicRadious = dynamicRadious + ((plottingView.frame.size.height / 2) - 60) / CGFloat(count + 1) 129 | i += 1 130 | } 131 | } 132 | 133 | private func drawCurvedString(on layer: CALayer, text: NSAttributedString, angle: CGFloat, radius: CGFloat, plottingViwe: UIView, isClockwise: Bool) { 134 | var radAngle = angle.radians 135 | 136 | let perimeter: CGFloat = 2 * .pi * radius 137 | 138 | var textRotation: CGFloat = 0 139 | var textDirection: CGFloat = 0 140 | 141 | if angle > CGFloat(10).radians, angle < CGFloat(170).radians { 142 | // bottom string 143 | textRotation = 0.5 * .pi 144 | textDirection = -2 * .pi 145 | radAngle = isClockwise ? angle/2 : -angle/2 //textAngle / 2 146 | } else { 147 | // top string 148 | textRotation = CGFloat(isClockwise ? (1.5 * .pi) : (1.5 * -.pi)) 149 | textDirection = CGFloat(isClockwise ? 2.0 * .pi : 2.0 * -.pi) 150 | radAngle = isClockwise ? angle/2 : -angle/2 //textAngle / 2 151 | } 152 | 153 | for c in 0.. CATextLayer { 181 | let textLayer = CATextLayer() 182 | textLayer.frame = frame 183 | textLayer.string = text 184 | //textLayer.backgroundColor = UIColor.yellow.cgColor 185 | textLayer.alignmentMode = CATextLayerAlignmentMode.left 186 | textLayer.contentsScale = UIScreen.main.scale 187 | return textLayer 188 | } 189 | } 190 | 191 | extension CGFloat { 192 | /** Degrees to Radian **/ 193 | var degrees: CGFloat { 194 | return self * (180.0 / .pi) 195 | } 196 | 197 | /** Radians to Degrees **/ 198 | var radians: CGFloat { 199 | return self / 180.0 * .pi 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /VennPiechart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thePankaj20/VennPieChart/f5495d41081f5749235ecc6ba8a213f24f9dcb5f/VennPiechart.png --------------------------------------------------------------------------------