Repository: FighterLightning/ChartsUnderstandAndUsage Branch: master Commit: afb59a2d5937 Files: 221 Total size: 1.1 MB Directory structure: gitextract_fw1y0vwm/ ├── ChartsUnderstandAndUsage/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── barChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── barChartWaveImage.imageset/ │ │ │ └── Contents.json │ │ ├── bubbleChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── candleStickChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── combinedChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── lineChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── lineFilledChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── pieChartHalfImage.imageset/ │ │ │ └── Contents.json │ │ ├── pieChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── pieChartPolylineImage.imageset/ │ │ │ └── Contents.json │ │ ├── radarChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── scatterChartImage.imageset/ │ │ │ └── Contents.json │ │ ├── smile.imageset/ │ │ │ └── Contents.json │ │ └── waveformChartImage.imageset/ │ │ └── Contents.json │ ├── BalloonMarker.swift │ ├── BarChartVC.swift │ ├── BarChartWaveVC.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── BaseVC.swift │ ├── BubbleChartVC.swift │ ├── CandleStickChartVC.swift │ ├── CombinedChartVC.swift │ ├── Info.plist │ ├── LineChartVC.swift │ ├── LineFilledChartVC.swift │ ├── PieChartHalfVC.swift │ ├── PieChartPolylineVC.swift │ ├── PieChartVC.swift │ ├── RadarChartVC.swift │ ├── ScatterChartVC.swift │ ├── ViewController.swift │ ├── WaveformChartVC.swift │ └── ZHFColor.swift ├── ChartsUnderstandAndUsage.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ └── zhanghaifeng.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ ├── macbook.xcuserdatad/ │ │ └── xcschemes/ │ │ └── xcschememanagement.plist │ └── zhanghaifeng.xcuserdatad/ │ └── xcschemes/ │ └── xcschememanagement.plist ├── ChartsUnderstandAndUsage.xcworkspace/ │ ├── contents.xcworkspacedata │ ├── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata/ │ ├── macbook.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── zhanghaifeng.xcuserdatad/ │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger/ │ └── Breakpoints_v2.xcbkptlist ├── ChartsUnderstandAndUsageTests/ │ ├── ChartsUnderstandAndUsageTests.swift │ └── Info.plist ├── ChartsUnderstandAndUsageUITests/ │ ├── ChartsUnderstandAndUsageUITests.swift │ └── Info.plist ├── Podfile ├── Pods/ │ ├── Charts/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── Source/ │ │ └── Charts/ │ │ ├── Animation/ │ │ │ ├── Animator.swift │ │ │ └── ChartAnimationEasing.swift │ │ ├── Charts/ │ │ │ ├── BarChartView.swift │ │ │ ├── BarLineChartViewBase.swift │ │ │ ├── BubbleChartView.swift │ │ │ ├── CandleStickChartView.swift │ │ │ ├── ChartViewBase.swift │ │ │ ├── CombinedChartView.swift │ │ │ ├── HorizontalBarChartView.swift │ │ │ ├── LineChartView.swift │ │ │ ├── PieChartView.swift │ │ │ ├── PieRadarChartViewBase.swift │ │ │ ├── RadarChartView.swift │ │ │ └── ScatterChartView.swift │ │ ├── Components/ │ │ │ ├── AxisBase.swift │ │ │ ├── ChartLimitLine.swift │ │ │ ├── ComponentBase.swift │ │ │ ├── Description.swift │ │ │ ├── IMarker.swift │ │ │ ├── Legend.swift │ │ │ ├── LegendEntry.swift │ │ │ ├── MarkerImage.swift │ │ │ ├── MarkerView.swift │ │ │ ├── XAxis.swift │ │ │ └── YAxis.swift │ │ ├── Data/ │ │ │ ├── Implementations/ │ │ │ │ ├── ChartBaseDataSet.swift │ │ │ │ └── Standard/ │ │ │ │ ├── BarChartData.swift │ │ │ │ ├── BarChartDataEntry.swift │ │ │ │ ├── BarChartDataSet.swift │ │ │ │ ├── BarLineScatterCandleBubbleChartData.swift │ │ │ │ ├── BarLineScatterCandleBubbleChartDataSet.swift │ │ │ │ ├── BubbleChartData.swift │ │ │ │ ├── BubbleChartDataEntry.swift │ │ │ │ ├── BubbleChartDataSet.swift │ │ │ │ ├── CandleChartData.swift │ │ │ │ ├── CandleChartDataEntry.swift │ │ │ │ ├── CandleChartDataSet.swift │ │ │ │ ├── ChartData.swift │ │ │ │ ├── ChartDataEntry.swift │ │ │ │ ├── ChartDataEntryBase.swift │ │ │ │ ├── ChartDataSet.swift │ │ │ │ ├── CombinedChartData.swift │ │ │ │ ├── LineChartData.swift │ │ │ │ ├── LineChartDataSet.swift │ │ │ │ ├── LineRadarChartDataSet.swift │ │ │ │ ├── LineScatterCandleRadarChartDataSet.swift │ │ │ │ ├── PieChartData.swift │ │ │ │ ├── PieChartDataEntry.swift │ │ │ │ ├── PieChartDataSet.swift │ │ │ │ ├── RadarChartData.swift │ │ │ │ ├── RadarChartDataEntry.swift │ │ │ │ ├── RadarChartDataSet.swift │ │ │ │ ├── ScatterChartData.swift │ │ │ │ └── ScatterChartDataSet.swift │ │ │ └── Interfaces/ │ │ │ ├── IBarChartDataSet.swift │ │ │ ├── IBarLineScatterCandleBubbleChartDataSet.swift │ │ │ ├── IBubbleChartDataSet.swift │ │ │ ├── ICandleChartDataSet.swift │ │ │ ├── IChartDataSet.swift │ │ │ ├── ILineChartDataSet.swift │ │ │ ├── ILineRadarChartDataSet.swift │ │ │ ├── ILineScatterCandleRadarChartDataSet.swift │ │ │ ├── IPieChartDataSet.swift │ │ │ ├── IRadarChartDataSet.swift │ │ │ └── IScatterChartDataSet.swift │ │ ├── Filters/ │ │ │ ├── DataApproximator+N.swift │ │ │ └── DataApproximator.swift │ │ ├── Formatters/ │ │ │ ├── DefaultAxisValueFormatter.swift │ │ │ ├── DefaultFillFormatter.swift │ │ │ ├── DefaultValueFormatter.swift │ │ │ ├── IAxisValueFormatter.swift │ │ │ ├── IFillFormatter.swift │ │ │ ├── IValueFormatter.swift │ │ │ └── IndexAxisValueFormatter.swift │ │ ├── Highlight/ │ │ │ ├── BarHighlighter.swift │ │ │ ├── ChartHighlighter.swift │ │ │ ├── CombinedHighlighter.swift │ │ │ ├── Highlight.swift │ │ │ ├── HorizontalBarHighlighter.swift │ │ │ ├── IHighlighter.swift │ │ │ ├── PieHighlighter.swift │ │ │ ├── PieRadarHighlighter.swift │ │ │ ├── RadarHighlighter.swift │ │ │ └── Range.swift │ │ ├── Interfaces/ │ │ │ ├── BarChartDataProvider.swift │ │ │ ├── BarLineScatterCandleBubbleChartDataProvider.swift │ │ │ ├── BubbleChartDataProvider.swift │ │ │ ├── CandleChartDataProvider.swift │ │ │ ├── ChartDataProvider.swift │ │ │ ├── CombinedChartDataProvider.swift │ │ │ ├── LineChartDataProvider.swift │ │ │ └── ScatterChartDataProvider.swift │ │ ├── Jobs/ │ │ │ ├── AnimatedMoveViewJob.swift │ │ │ ├── AnimatedViewPortJob.swift │ │ │ ├── AnimatedZoomViewJob.swift │ │ │ ├── MoveViewJob.swift │ │ │ ├── ViewPortJob.swift │ │ │ └── ZoomViewJob.swift │ │ ├── Renderers/ │ │ │ ├── AxisRendererBase.swift │ │ │ ├── BarChartRenderer.swift │ │ │ ├── BarLineScatterCandleBubbleRenderer.swift │ │ │ ├── BubbleChartRenderer.swift │ │ │ ├── CandleStickChartRenderer.swift │ │ │ ├── ChartDataRendererBase.swift │ │ │ ├── CombinedChartRenderer.swift │ │ │ ├── HorizontalBarChartRenderer.swift │ │ │ ├── LegendRenderer.swift │ │ │ ├── LineChartRenderer.swift │ │ │ ├── LineRadarRenderer.swift │ │ │ ├── LineScatterCandleRadarRenderer.swift │ │ │ ├── PieChartRenderer.swift │ │ │ ├── RadarChartRenderer.swift │ │ │ ├── Renderer.swift │ │ │ ├── Scatter/ │ │ │ │ ├── ChevronDownShapeRenderer.swift │ │ │ │ ├── ChevronUpShapeRenderer.swift │ │ │ │ ├── CircleShapeRenderer.swift │ │ │ │ ├── CrossShapeRenderer.swift │ │ │ │ ├── IShapeRenderer.swift │ │ │ │ ├── SquareShapeRenderer.swift │ │ │ │ ├── TriangleShapeRenderer.swift │ │ │ │ └── XShapeRenderer.swift │ │ │ ├── ScatterChartRenderer.swift │ │ │ ├── XAxisRenderer.swift │ │ │ ├── XAxisRendererHorizontalBarChart.swift │ │ │ ├── XAxisRendererRadarChart.swift │ │ │ ├── YAxisRenderer.swift │ │ │ ├── YAxisRendererHorizontalBarChart.swift │ │ │ └── YAxisRendererRadarChart.swift │ │ └── Utils/ │ │ ├── ChartColorTemplates.swift │ │ ├── ChartUtils.swift │ │ ├── Fill.swift │ │ ├── Platform.swift │ │ ├── Transformer.swift │ │ ├── TransformerHorizontalBarChart.swift │ │ └── ViewPortHandler.swift │ ├── Pods.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcuserdata/ │ │ ├── macbook.xcuserdatad/ │ │ │ └── xcschemes/ │ │ │ ├── Charts.xcscheme │ │ │ ├── Pods-ChartsUnderstandAndUsage.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── zhanghaifeng.xcuserdatad/ │ │ └── xcschemes/ │ │ ├── Charts.xcscheme │ │ ├── Pods-ChartsUnderstandAndUsage.xcscheme │ │ └── xcschememanagement.plist │ └── Target Support Files/ │ ├── Charts/ │ │ ├── Charts-dummy.m │ │ ├── Charts-prefix.pch │ │ ├── Charts-umbrella.h │ │ ├── Charts.debug.xcconfig │ │ ├── Charts.modulemap │ │ ├── Charts.release.xcconfig │ │ ├── Charts.xcconfig │ │ └── Info.plist │ └── Pods-ChartsUnderstandAndUsage/ │ ├── Info.plist │ ├── Pods-ChartsUnderstandAndUsage-Info.plist │ ├── Pods-ChartsUnderstandAndUsage-acknowledgements.markdown │ ├── Pods-ChartsUnderstandAndUsage-acknowledgements.plist │ ├── Pods-ChartsUnderstandAndUsage-dummy.m │ ├── Pods-ChartsUnderstandAndUsage-frameworks-Debug-input-files.xcfilelist │ ├── Pods-ChartsUnderstandAndUsage-frameworks-Debug-output-files.xcfilelist │ ├── Pods-ChartsUnderstandAndUsage-frameworks-Release-input-files.xcfilelist │ ├── Pods-ChartsUnderstandAndUsage-frameworks-Release-output-files.xcfilelist │ ├── Pods-ChartsUnderstandAndUsage-frameworks.sh │ ├── Pods-ChartsUnderstandAndUsage-resources.sh │ ├── Pods-ChartsUnderstandAndUsage-umbrella.h │ ├── Pods-ChartsUnderstandAndUsage.debug.xcconfig │ ├── Pods-ChartsUnderstandAndUsage.modulemap │ └── Pods-ChartsUnderstandAndUsage.release.xcconfig └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: ChartsUnderstandAndUsage/AppDelegate.swift ================================================ // AppDelegate.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/12. // Copyright © 2018年 张海峰. All rights reserved. import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } } //定义一个全局函数打印方法 //自定义标记 ->项目 ->buildSettings -> swift flag ->Debug -> -D DEBUG func ZHFLog(message : T, file : String = #file, line : Int = #line) { //在DEBUG环境下打印,在RELEASE环境下不打印 #if DEBUG let file1 = (file as NSString).lastPathComponent let line1 = (line as Int) print("\(file1):line\(line1)---\(message)") #endif } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/barChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "barChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/barChartWaveImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "barChartWaveImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/bubbleChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "bubbleChartImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/candleStickChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "candleStickChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/combinedChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "combinedChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/lineChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "lineChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/lineFilledChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "LineFilledChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/pieChartHalfImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "pieChartHalfImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/pieChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "pieChartImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/pieChartPolylineImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "pieChartPolylineImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/radarChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "radarChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/scatterChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "scatterChartImage.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/smile.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "smile.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/Assets.xcassets/waveformChartImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "waveformChartImage.png", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ChartsUnderstandAndUsage/BalloonMarker.swift ================================================ // // BalloonMarker.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/18. // Copyright © 2018年 张海峰. All rights reserved. // import UIKit import Charts class BalloonMarker: MarkerImage { open var color: UIColor open var arrowSize = CGSize(width: 15, height: 11) open var font: UIFont open var textColor: UIColor open var insets: UIEdgeInsets open var minimumSize = CGSize() fileprivate var label: String? fileprivate var _labelSize: CGSize = CGSize() fileprivate var _paragraphStyle: NSMutableParagraphStyle? fileprivate var _drawAttributes = [NSAttributedString.Key : AnyObject]() public init(color: UIColor, font: UIFont, textColor: UIColor, insets: UIEdgeInsets) { self.color = color self.font = font self.textColor = textColor self.insets = insets _paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle _paragraphStyle?.alignment = .center super.init() } open override func offsetForDrawing(atPoint point: CGPoint) -> CGPoint { var offset = self.offset var size = self.size if size.width == 0.0 && image != nil { size.width = image!.size.width } if size.height == 0.0 && image != nil { size.height = image!.size.height } let width = size.width let height = size.height let padding: CGFloat = 8.0 var origin = point origin.x -= width / 2 origin.y -= height if origin.x + offset.x < 0.0 { offset.x = -origin.x + padding } else if let chart = chartView, origin.x + width + offset.x > chart.bounds.size.width { offset.x = chart.bounds.size.width - origin.x - width - padding } if origin.y + offset.y < 0 { offset.y = height + padding; } else if let chart = chartView, origin.y + height + offset.y > chart.bounds.size.height { offset.y = chart.bounds.size.height - origin.y - height - padding } return offset } open override func draw(context: CGContext, point: CGPoint) { guard let label = label else { return } let offset = self.offsetForDrawing(atPoint: point) let size = self.size var rect = CGRect( origin: CGPoint( x: point.x + offset.x, y: point.y + offset.y), size: size) rect.origin.x -= size.width / 2.0 rect.origin.y -= size.height context.saveGState() context.setFillColor(color.cgColor) if offset.y > 0 { context.beginPath() context.move(to: CGPoint( x: rect.origin.x, y: rect.origin.y + arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0, y: rect.origin.y + arrowSize.height)) //arrow vertex context.addLine(to: CGPoint( x: point.x, y: point.y)) context.addLine(to: CGPoint( x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0, y: rect.origin.y + arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y + arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y + rect.size.height)) context.addLine(to: CGPoint( x: rect.origin.x, y: rect.origin.y + rect.size.height)) context.addLine(to: CGPoint( x: rect.origin.x, y: rect.origin.y + arrowSize.height)) context.fillPath() } else { context.beginPath() context.move(to: CGPoint( x: rect.origin.x, y: rect.origin.y)) context.addLine(to: CGPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y)) context.addLine(to: CGPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y + rect.size.height - arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0, y: rect.origin.y + rect.size.height - arrowSize.height)) //arrow vertex context.addLine(to: CGPoint( x: point.x, y: point.y)) context.addLine(to: CGPoint( x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0, y: rect.origin.y + rect.size.height - arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x, y: rect.origin.y + rect.size.height - arrowSize.height)) context.addLine(to: CGPoint( x: rect.origin.x, y: rect.origin.y)) context.fillPath() } if offset.y > 0 { rect.origin.y += self.insets.top + arrowSize.height } else { rect.origin.y += self.insets.top } rect.size.height -= self.insets.top + self.insets.bottom UIGraphicsPushContext(context) label.draw(in: rect, withAttributes: _drawAttributes) UIGraphicsPopContext() context.restoreGState() } /** open override func refreshContent(entry: ChartDataEntry, highlight: Highlight) { setLabel(String(entry.y)) } **/ open func setLabel(_ newLabel: String) { label = newLabel _drawAttributes.removeAll() _drawAttributes[.font] = self.font _drawAttributes[.paragraphStyle] = _paragraphStyle _drawAttributes[.foregroundColor] = self.textColor _labelSize = label?.size(withAttributes: _drawAttributes) ?? CGSize.zero var size = CGSize() size.width = _labelSize.width + self.insets.left + self.insets.right size.height = _labelSize.height + self.insets.top + self.insets.bottom size.width = max(minimumSize.width, size.width) size.height = max(minimumSize.height, size.height) self.size = size } } ================================================ FILE: ChartsUnderstandAndUsage/BarChartVC.swift ================================================ // // BarChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/12. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*柱状图*/ import UIKit import Charts class BarChartVC: BaseVC { //默认是垂直方向 把下面这一行的 BarChartView 改为 HorizontalBarChartView 即为水平方向 var barChartView: BarChartView = BarChartView() lazy var xVals: NSMutableArray = NSMutableArray.init() var data: BarChartData = BarChartData() let axisMaximum :Double = 100 override func viewDidLoad() { super.viewDidLoad() //添加柱状图 addBarChartView() //设置基本样式 setBarChartViewBaseStyle() //设置X轴,Y轴样式 setBarChartViewXY() //添加(刷新数据) updataData() } //添加柱状图 func addBarChartView(){ barChartView.backgroundColor = ZHFColor.white barChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) barChartView.center = self.view.center barChartView.delegate = self self.view.addSubview(barChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setBarChartViewBaseStyle(){ //基本样式 barChartView.noDataText = "暂无数据"//没有数据时的显示 barChartView.drawValueAboveBarEnabled = true//数值显示是否在条柱上面 barChartView.drawBarShadowEnabled = false//是否绘制阴影背景 //交互设置 (把煮食逐个取消试试) // barChartView.scaleXEnabled = false//取消X轴缩放 barChartView.scaleYEnabled = false//取消Y轴缩放 barChartView.doubleTapToZoomEnabled = false//取消双击是否缩放 // barChartView.pinchZoomEnabled = false//取消XY轴是否同时缩放 barChartView.dragEnabled = true //启用拖拽图表 barChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果 barChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果的摩擦系数(0~1),数值越小,惯性越不明显 } func setBarChartViewXY(){ //1.X轴样式设置(对应界面显示的--->0月到7月) let xAxis: XAxis = barChartView.xAxis xAxis.valueFormatter = self //重写代理方法 设置x轴数据 xAxis.axisLineWidth = 1 //设置X轴线宽 xAxis.labelPosition = XAxis.LabelPosition.bottom //X轴(5种位置显示,根据需求进行设置) xAxis.drawGridLinesEnabled = false//不绘制网格 xAxis.labelWidth = 4 //设置label间隔,若设置为1,则如果能全部显示,则每个柱形下面都会显示label xAxis.labelFont = UIFont.systemFont(ofSize: 10)//x轴数值字体大小 xAxis.labelTextColor = ZHFColor.brown//数值字体颜色 //2.Y轴左样式设置(对应界面显示的--->0 到 100) let leftAxisFormatter = NumberFormatter() leftAxisFormatter.minimumFractionDigits = 0 leftAxisFormatter.maximumFractionDigits = 1 leftAxisFormatter.positiveSuffix = " $" //数字前缀positivePrefix、 后缀positiveSuffix let leftAxis: YAxis = barChartView.leftAxis leftAxis.valueFormatter = DefaultAxisValueFormatter.init(formatter: leftAxisFormatter) leftAxis.axisMinimum = 0 //最小值 leftAxis.axisMaximum = axisMaximum //最大值 leftAxis.forceLabelsEnabled = true //不强制绘制制定数量的label leftAxis.labelCount = 6 //Y轴label数量,数值不一定,如果forceLabelsEnabled等于true, 则强制绘制制定数量的label, 但是可能不平均 leftAxis.inverted = false //是否将Y轴进行上下翻转 leftAxis.axisLineWidth = 0.5 //Y轴线宽 leftAxis.axisLineColor = ZHFColor.black //Y轴颜色 leftAxis.labelPosition = YAxis.LabelPosition.outsideChart//坐标数值的位置 leftAxis.labelTextColor = ZHFColor.brown//坐标数值字体颜色 leftAxis.labelFont = UIFont.systemFont(ofSize: 10) //y轴字体大小 //设置虚线样式的网格线(对应的是每条横着的虚线[10.0, 3.0]对应实线和虚线的长度) leftAxis.drawGridLinesEnabled = true //是否绘制网格线(默认为true) leftAxis.gridLineDashLengths = [10.0, 3.0] leftAxis.gridColor = ZHFColor.gray //网格线颜色 leftAxis.gridAntialiasEnabled = true//开启抗锯齿 leftAxis.spaceTop = 0.15//最大值到顶部的范围比 //设置限制线 let limitLine : ChartLimitLine = ChartLimitLine.init(limit: Double(axisMaximum * 0.85), label: "限制线") limitLine.lineWidth = 2 limitLine.lineColor = ZHFColor.green limitLine.lineDashLengths = [5.0, 2.0] limitLine.labelPosition = ChartLimitLine.LabelPosition.rightTop//位置 limitLine.valueTextColor = ZHFColor.zhf66_contentTextColor limitLine.valueFont = UIFont.systemFont(ofSize: 12) leftAxis.addLimitLine(limitLine) leftAxis.drawLimitLinesBehindDataEnabled = true //设置限制线在柱线图后面(默认在前) //3.Y轴右样式设置(如若设置可参考左样式) barChartView.rightAxis.enabled = false //不绘制右边轴线 //4.描述文字设置 barChartView.chartDescription?.text = "柱形图"//右下角的description文字样式 不设置的话会有默认数据 barChartView.chartDescription?.position = CGPoint.init(x: 80, y: 5)//位置(及在barChartView的中心点) barChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 barChartView.chartDescription?.textColor = ZHFColor.orange //5.设置类型试图的对齐方式,右上角 (默认左下角) let legend = barChartView.legend legend.enabled = true legend.horizontalAlignment = .right legend.verticalAlignment = .top legend.orientation = .horizontal legend.textColor = ZHFColor.orange legend.font = UIFont.systemFont(ofSize: 11.0) } //设置数据 @objc func updataData(){ //对应x轴上面需要显示的数据 let count = 8 let x1Vals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { //x轴字体展示 x1Vals.add("\(i)月") self.xVals = x1Vals } //对应Y轴上面需要显示的数据 let yVals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { let val: Double = Double(arc4random_uniform(UInt32(axisMaximum))) let entry:BarChartDataEntry = BarChartDataEntry.init(x: Double(i), y: Double(val)) yVals.add(entry) } //创建BarChartDataSet对象,其中包含有Y轴数据信息,以及可以设置柱形样式 let set1: BarChartDataSet = BarChartDataSet.init(values: yVals as? [ChartDataEntry], label: "信息") set1.barBorderWidth = 0.2 //边线宽 set1.drawValuesEnabled = true //是否在柱形图上面显示数值 set1.highlightEnabled = true //点击选中柱形图是否有高亮效果,(单击空白处取消选中) set1.setColors(ZHFColor.gray,ZHFColor.green,ZHFColor.yellow,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置柱形图颜色(是一个循环,例如:你设置5个颜色,你设置8个柱形,后三个对应的颜色是该设置中的前三个,依次类推) // set1.setColors(ChartColorTemplates.material(), alpha: 1) // set1.setColor(ZHFColor.gray)//颜色一致 let dataSets: NSMutableArray = NSMutableArray.init() dataSets.add(set1) //创建BarChartData对象, 此对象就是barChartView需要最终数据对象 let data: BarChartData = BarChartData.init(dataSets: dataSets as? [IChartDataSet]) data.barWidth = 0.7 //默认是0.85 (介于0-1之间) data.setValueFont(UIFont.systemFont(ofSize: 10)) data.setValueTextColor(ZHFColor.orange) let formatter: NumberFormatter = NumberFormatter.init() formatter.numberStyle = NumberFormatter.Style.currency//自定义数据显示格式 小数点形式(可以尝试不同看效果) let forma :DefaultValueFormatter = DefaultValueFormatter.init(formatter: formatter) data.setValueFormatter(forma) barChartView.data = data barChartView.animate(yAxisDuration: 1)//展示方式xAxisDuration 和 yAxisDuration两种 // barChartView.animate(xAxisDuration: 2, yAxisDuration: 2)//展示方式xAxisDuration 和 yAxisDuration两种 } } //MARK:- extension BarChartVC :ChartViewDelegate,IAxisValueFormatter { func stringForValue(_ value: Double, axis: AxisBase?) -> String { return self.xVals[Int(value)] as! String } //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/BarChartWaveVC.swift ================================================ // // BarChartHorizontalVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/10/18. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*柱状图(波浪)*/ import UIKit import Charts class BarChartWaveVC: BaseVC { var barChartView: BarChartView = BarChartView() lazy var xVals: NSMutableArray = NSMutableArray.init() var data: BarChartData = BarChartData() let axisMaximum :Double = 100 override func viewDidLoad() { super.viewDidLoad() //添加柱状图 addBarChartView() //设置基本样式 setBarChartViewBaseStyle() //设置X轴,Y轴样式 setBarChartViewXY() //添加(刷新数据) updataData() } //添加柱状图 func addBarChartView(){ barChartView.backgroundColor = ZHFColor.white barChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) barChartView.center = self.view.center barChartView.delegate = self self.view.addSubview(barChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setBarChartViewBaseStyle(){ //基本样式 barChartView.noDataText = "暂无数据"//没有数据时的显示 barChartView.drawValueAboveBarEnabled = true//数值显示是否在条柱上面 barChartView.drawBarShadowEnabled = false//是否绘制阴影背景 //交互设置 (把煮食逐个取消试试) // barChartView.scaleXEnabled = false//取消X轴缩放 barChartView.scaleYEnabled = false//取消Y轴缩放 barChartView.doubleTapToZoomEnabled = false//取消双击是否缩放 // barChartView.pinchZoomEnabled = false//取消XY轴是否同时缩放 barChartView.dragEnabled = true //启用拖拽图表 barChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果 barChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果的摩擦系数(0~1),数值越小,惯性越不明显 } func setBarChartViewXY(){ //1.X轴样式设置(对应界面显示的--->0月到7月) let xAxis: XAxis = barChartView.xAxis xAxis.axisLineWidth = 1 //设置X轴线宽 xAxis.labelPosition = XAxis.LabelPosition.bottom //X轴(5种位置显示,根据需求进行设置) xAxis.drawGridLinesEnabled = false//不绘制网格 xAxis.labelWidth = 4 //设置label间隔,若设置为1,则如果能全部显示,则每个柱形下面都会显示label xAxis.labelFont = UIFont.systemFont(ofSize: 10)//x轴数值字体大小 xAxis.labelTextColor = ZHFColor.brown//数值字体颜色 //2.Y轴左样式设置(对应界面显示的--->-2.5 到 2.5) let leftAxis = barChartView.leftAxis leftAxis.labelCount = 8 leftAxis.axisMinimum = -1.5 leftAxis.axisMaximum = 1.5 leftAxis.granularityEnabled = true leftAxis.granularity = 0.2 //左Y 轴线间隙 //3.Y轴右样式设置(如若设置可参考左样式) let rightAxis = barChartView.rightAxis rightAxis.labelCount = 8 rightAxis.axisMinimum = -1.5 rightAxis.axisMaximum = 1.5 rightAxis.granularity = 0.2 //右Y 轴线间隙 //4.描述文字设置 barChartView.chartDescription?.text = "柱形图"//右下角的description文字样式 不设置的话会有默认数据 barChartView.chartDescription?.position = CGPoint.init(x: 80, y: 5)//位置(及在barChartView的中心点) barChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 barChartView.chartDescription?.textColor = ZHFColor.orange //5.设置类型试图的对齐方式,右上角 (默认左下角) let legend = barChartView.legend legend.enabled = true legend.horizontalAlignment = .right legend.verticalAlignment = .top legend.orientation = .horizontal legend.textColor = ZHFColor.orange legend.font = UIFont.systemFont(ofSize: 11.0) } @objc func updataData(){ let count: NSInteger = NSInteger(arc4random_uniform(UInt32(150)) + 100) let entries = (0 ..< count).map { BarChartDataEntry(x: Double($0), y: sin(.pi * Double($0%128) / 64)) } let set = BarChartDataSet(values: entries, label: "信息") set.setColor(UIColor(red: 240/255, green: 120/255, blue: 123/255, alpha: 1)) let data = BarChartData(dataSet: set) data.setValueFont(.systemFont(ofSize: 10, weight: .light)) data.setDrawValues(false) data.barWidth = 0.8 barChartView.data = data barChartView.animate(xAxisDuration: 2, yAxisDuration: 2)//展示方式xAxisDuration 和 yAxisDuration两种 } } //MARK:- extension BarChartWaveVC :ChartViewDelegate { //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: ChartsUnderstandAndUsage/Base.lproj/Main.storyboard ================================================ ================================================ FILE: ChartsUnderstandAndUsage/BaseVC.swift ================================================ // // BaseVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/10/18. // Copyright © 2018年 张海峰. All rights reserved. // import UIKit class BaseVC: UIViewController { var refreshrBtn: UIButton = UIButton() override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = ZHFColor.white //添加刷新按钮 refreshrBtn = UIButton.init(type: .custom) refreshrBtn.frame = CGRect.init(x: 30, y: 84, width: 40, height: 25) refreshrBtn.setTitle("刷新", for: .normal) refreshrBtn.backgroundColor = ZHFColor.red refreshrBtn.setTitleColor(ZHFColor.zhf33_titleTextColor, for: .normal) refreshrBtn.layer.cornerRadius = 5 refreshrBtn.titleLabel?.font = UIFont.systemFont(ofSize: 13) self.view.addSubview(refreshrBtn) } } ================================================ FILE: ChartsUnderstandAndUsage/BubbleChartVC.swift ================================================ // // BubbleChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/17. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*气泡图*/ import UIKit import Charts class BubbleChartVC: BaseVC { var bubbleChartView: BubbleChartView = BubbleChartView() override func viewDidLoad() { super.viewDidLoad() //1.添加气泡图 addBubbleChart() //2. 基本样式 setBubbleChartViewBaseStyle() //3.添加(刷新数据) updataData() } } extension BubbleChartVC{ //添加气泡图 func addBubbleChart(){ bubbleChartView.backgroundColor = ZHFColor.white bubbleChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) bubbleChartView.center = self.view.center bubbleChartView.delegate = self self.view.addSubview(bubbleChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setBubbleChartViewBaseStyle(){ //气泡图描述 bubbleChartView.chartDescription?.text = "气泡图描述" bubbleChartView.chartDescription?.position = CGPoint.init(x: bubbleChartView.frame.width - 30, y:bubbleChartView.frame.height - 20)//位置(及在bubbleChartView的中心点) bubbleChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 bubbleChartView.chartDescription?.textColor = UIColor.red //图例 let l = bubbleChartView.legend l.wordWrapEnabled = false //显示图例 l.horizontalAlignment = .left //居左 l.verticalAlignment = .bottom //放在底部 l.orientation = .horizontal //水平排布 l.drawInside = false // 图例在外 l.formSize = 10 //(图例大小)默认是8 l.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) //Y轴右侧线 let rightAxis = bubbleChartView.rightAxis rightAxis.axisMinimum = 0 //Y轴左侧线 let leftAxis = bubbleChartView.leftAxis leftAxis.axisMinimum = 0 //X轴 let xAxis = bubbleChartView.xAxis xAxis.labelPosition = .bothSided //分布在两边外部 xAxis.axisMinimum = 0 //最小刻度值 xAxis.granularity = 1 //最小间隔 } @objc func updataData(){ //第一组气泡图的10条随机数据 let dataEntries1 = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(50)) let size = CGFloat(arc4random_uniform(10)) //只要size超过6的气泡都会带有一个小图标 if size > 6 { return BubbleChartDataEntry(x: Double(i), y: val, size: size, icon: UIImage(named: "smile"))//这个图片可根据需求定 } else { return BubbleChartDataEntry(x: Double(i), y: val, size: size) } } let chartDataSet1 = BubbleChartDataSet(values: dataEntries1, label: "气泡1") chartDataSet1.highlightCircleWidth = 6 //气泡选中时的边框宽 chartDataSet1.iconsOffset = CGPoint(x: 10, y: -10) //修改气泡上的图片位置(默认居中) chartDataSet1.drawValuesEnabled = true chartDataSet1.valueFont = UIFont.systemFont(ofSize: 7) chartDataSet1.valueTextColor = ZHFColor.red //第二组气泡图的10条随机数据 let dataEntries2 = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(50) + 50) let size = CGFloat(arc4random_uniform(10)) //只要size超过6的气泡都会带有一个小图标 if size > 6 { return BubbleChartDataEntry(x: Double(i), y: val, size: size, icon: UIImage(named: "smile"))//这个图片可根据需求定 } else { return BubbleChartDataEntry(x: Double(i), y: val, size: size) } } let chartDataSet2 = BubbleChartDataSet(values: dataEntries2, label: "气泡2") chartDataSet2.setColor(.orange) //第二组气泡使用橙色 //目前气泡图包括2组数据 let chartData = BubbleChartData(dataSets: [chartDataSet1, chartDataSet2]) //设置气泡图数据 bubbleChartView.data = chartData } } extension BubbleChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/CandleStickChartVC.swift ================================================ // // CandleStickChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/17. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*K 线图(烛形图)*/ import UIKit import Charts class CandleStickChartVC: BaseVC { var candleStickChartView: CandleStickChartView = CandleStickChartView() override func viewDidLoad() { super.viewDidLoad() //1.添加K 线图(烛形图) addCandleStickChart() //2. 基本样式 setCandleStickChartViewBaseStyle() //3.添加(刷新数据) updataData() } } extension CandleStickChartVC{ //添加K 线图(烛形图) func addCandleStickChart(){ candleStickChartView.backgroundColor = ZHFColor.white candleStickChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) candleStickChartView.center = self.view.center candleStickChartView.delegate = self self.view.addSubview(candleStickChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setCandleStickChartViewBaseStyle(){ //K 线图(烛形图)描述 candleStickChartView.chartDescription?.text = "K 线图(烛形图)描述" candleStickChartView.chartDescription?.position = CGPoint.init(x: candleStickChartView.frame.width - 30, y:candleStickChartView.frame.height - 20)//位置(及在bubbleChartView的中心点) candleStickChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 candleStickChartView.chartDescription?.textColor = UIColor.red //图例 let l = candleStickChartView.legend l.wordWrapEnabled = false //显示图例 l.horizontalAlignment = .left //居左 l.verticalAlignment = .bottom //放在底部 l.orientation = .horizontal //水平排布 l.drawInside = false // 图例在外 l.formSize = 10 //(图例大小)默认是8 l.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) //Y轴右侧线 let rightAxis = candleStickChartView.rightAxis rightAxis.axisMinimum = 0 //Y轴左侧线 let leftAxis = candleStickChartView.leftAxis leftAxis.axisMinimum = 0 //X轴 let xAxis = candleStickChartView.xAxis xAxis.labelPosition = .bothSided //分布在两边外部 xAxis.axisMinimum = 0 //最小刻度值 xAxis.granularity = 1 //最小间隔 } @objc func updataData(){ //第一组烛形图的10条随机数据 let dataEntries1 = (0..<10).map { (i) -> CandleChartDataEntry in let val = Double(arc4random_uniform(40) + 10) let high = Double(arc4random_uniform(9) + 8) let low = Double(arc4random_uniform(9) + 8) let open = Double(arc4random_uniform(6) + 1) let close = Double(arc4random_uniform(6) + 1) let even = arc4random_uniform(2) % 2 == 0 //true表示开盘价高于收盘价 //当天涨幅超过9的显示一个星星图标 if(!even && (open + close) > 9 ){ return CandleChartDataEntry(x: Double(i), shadowH: val + high, shadowL: val - low, open: even ? val + open : val - open, close: even ? val - close : val + close, icon: UIImage(named: "smile")!) } else{ return CandleChartDataEntry(x: Double(i), shadowH: val + high, shadowL: val - low, open: even ? val + open : val - open, close: even ? val - close : val + close) } } let chartDataSet1 = CandleChartDataSet(values: dataEntries1, label: "图例1") chartDataSet1.shadowWidth = 2 //柱线(烛心线)颜色 chartDataSet1.decreasingFilled = false //开盘高于收盘则使用空心矩形 chartDataSet1.increasingFilled = true //开盘低于收盘则使用实心矩形 chartDataSet1.setColor(.orange) //整体设置颜色 // chartDataSet1.shadowColor = .darkGray //柱线(烛心线)颜色 // chartDataSet1.decreasingColor = ZHFColor.green //实心颜色 // chartDataSet1.increasingColor = ZHFColor.red //空心颜色 // chartDataSet1.shadowColorSameAsCandle = true//竖线的颜色与方框颜色一样 //chartDataSet1.showCandleBar = false //不显示方块 //目前烛形图包括1组数据 let chartData = CandleChartData(dataSets: [chartDataSet1]) //设置烛形图数据 candleStickChartView.data = chartData } } extension CandleStickChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/CombinedChartVC.swift ================================================ // // CombinedChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/17. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*组合图*/ import UIKit import Charts class CombinedChartVC: BaseVC { let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] var combinedChartView: CombinedChartView = CombinedChartView() override func viewDidLoad() { super.viewDidLoad() //1.添加混合图 addCombinedChart() //2.基本样式 setCombinedChartViewBaseStyle() //3.添加(刷新数据) updataData() } } //MARK:- UI和组合图基本样式 extension CombinedChartVC{ //添加组合线图 func addCombinedChart(){ combinedChartView.backgroundColor = ZHFColor.white combinedChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) combinedChartView.center = self.view.center combinedChartView.delegate = self self.view.addSubview(combinedChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setCombinedChartViewBaseStyle(){ //混合图描述 combinedChartView.chartDescription?.text = "混合图描述" combinedChartView.chartDescription?.position = CGPoint.init(x: combinedChartView.frame.width - 30, y:10)//位置(及在lineChartView的中心点) combinedChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 combinedChartView.chartDescription?.textColor = UIColor.red //图例 let l = combinedChartView.legend l.wordWrapEnabled = false //显示图例 l.horizontalAlignment = .center //居中 l.verticalAlignment = .bottom //放在底部 l.orientation = .horizontal //水平排布 l.drawInside = false // 图例在外 l.formSize = 10 //(图例大小)默认是8 l.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) //Y轴右侧线 let rightAxis = combinedChartView.rightAxis rightAxis.axisMinimum = 0 //Y轴左侧线 let leftAxis = combinedChartView.leftAxis leftAxis.axisMinimum = 0 //X轴 let xAxis = combinedChartView.xAxis xAxis.labelPosition = .bothSided //分布在两边外部 xAxis.axisMinimum = 0 //最小刻度值 xAxis.granularity = 1 //最小间隔 xAxis.valueFormatter = self } } //MARK:- 数据加载和刷新 extension CombinedChartVC{ @objc func updataData(){ //各类型图表的显示次序(后面的覆盖前面的) combinedChartView.drawOrder = [DrawOrder.bar.rawValue, DrawOrder.bubble.rawValue, DrawOrder.line.rawValue, DrawOrder.scatter.rawValue, DrawOrder.candle.rawValue] //组合图数据 let chartData = CombinedChartData() chartData.barData = generateBarData() //柱形图数据 chartData.lineData = generateLineData() //线状图数据 chartData.scatterData = generateScatterData() // chartData.bubbleData = generateBubbleData() chartData.candleData = generateCandleData() //烛形图(股票线) //设置组合图数据 combinedChartView.xAxis.axisMaximum = chartData.xMax + 0.25 combinedChartView.data = chartData } //柱状图数据 func generateBarData() -> BarChartData { // 第一根柱 //10条随机数据 var dataEntries1 = [BarChartDataEntry]() for i in 0..<10 { let y = arc4random()%100 + 100 let entry = BarChartDataEntry(x: Double(i), y: Double(y)) dataEntries1.append(entry) } //这10条数据作为柱状图的所有数据 let chartDataSet1 = BarChartDataSet(values: dataEntries1, label: "柱状图1") chartDataSet1.valueFont = UIFont.systemFont(ofSize: 10) chartDataSet1.colors = [.orange] //使用橙色 chartDataSet1.axisDependency = .left //依赖左侧轴 chartDataSet1.drawValuesEnabled = false //不带文字 // 第二根柱 //生成10条随机数据 var dataEntries2 = [BarChartDataEntry]() for i in 0..<10 { let y = arc4random()%50 + 50 let y1 = arc4random()%50 + 50 let entry = BarChartDataEntry.init(x: Double(i), yValues: [Double(y),Double(y1)]) dataEntries2.append(entry) } //这10条数据作为柱状图的所有数据 let chartDataSet2 = BarChartDataSet(values: dataEntries2, label: "柱状图2") chartDataSet2.stackLabels = ["Stack 1", "Stack 2"] chartDataSet2.valueFont = UIFont.systemFont(ofSize: 10) chartDataSet2.colors = [.red,.orange] //使用红色 chartDataSet2.axisDependency = .left //依赖左侧轴 chartDataSet2.drawValuesEnabled = false //不带文字 //目前柱状图只包括1组立柱 let chartData = BarChartData(dataSets: [chartDataSet1,chartDataSet2]) let groupSpace = 0.06 //组与组之间的空间比 let barSpace = 0.02 // 一组内两柱之间空间比 let barWidth = 0.45 // 一组内每个柱宽空间比 // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" chartData.barWidth = barWidth // make this BarData object grouped chartData.groupBars(fromX: 0, groupSpace: groupSpace, barSpace: barSpace) return chartData } //折线图数据 func generateLineData() -> LineChartData { //生成10条随机数据 var dataEntries = [ChartDataEntry]() for i in 0..<10 { let y = arc4random()%100 let entry = ChartDataEntry(x: Double(i), y: Double(y)) dataEntries.append(entry) } //这10条数据作为折线图的所有数据 let chartDataSet = LineChartDataSet(values: dataEntries, label: "折线图") chartDataSet.setColor(ZHFColor.zhf_randomColor())//折线颜色 chartDataSet.lineWidth = 2.5 chartDataSet.setCircleColor(ZHFColor.red) chartDataSet.circleRadius = 5 //外圆半径 chartDataSet.circleHoleColor = ZHFColor.yellow chartDataSet.circleHoleRadius = 2.5 //内圆半径 // chartDataSet.drawFilledEnabled = true //开启填充色绘制 // chartDataSet.fillColor = ZHFColor.zhf_randomColor() // chartDataSet.fillAlpha = 0.5 //设置填充色透明度 chartDataSet.mode = .cubicBezier //折线是bezier曲线 chartDataSet.drawValuesEnabled = true //带文字 chartDataSet.valueFont = .systemFont(ofSize: 10) chartDataSet.valueTextColor = ZHFColor.zhf_randomColor() chartDataSet.axisDependency = .left//依赖左侧轴 //目前柱状图只包括1组折线 let chartData = LineChartData(dataSets: [chartDataSet]) return chartData } //生成散点图数据 func generateScatterData() -> ScatterChartData { //生成10条随机数据 let dataEntries = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(100) + 150) return ChartDataEntry(x: Double(i), y: val) } let chartDataSet = ScatterChartDataSet(values: dataEntries, label: "散点图") chartDataSet.colors = [ChartColorTemplates.material()[0]] chartDataSet.scatterShapeSize = 8 //(默认是10) chartDataSet.drawValuesEnabled = false //目前散点图包括1组数据 let chartData = ScatterChartData(dataSets: [chartDataSet]) return chartData } //生成气泡图数据 func generateBubbleData() -> BubbleChartData { //生成10条随机数据 let dataEntries = (0..<10).map { (i) -> BubbleChartDataEntry in let val = Double(arc4random_uniform(100) + 300) //气泡大小 let size = CGFloat(arc4random_uniform(10)) return BubbleChartDataEntry(x: Double(i), y: val, size: size) } let chartDataSet = BubbleChartDataSet(values: dataEntries, label: "气泡图") chartDataSet.colors = [ChartColorTemplates.material()[1]] chartDataSet.valueTextColor = .white chartDataSet.valueFont = .systemFont(ofSize: 10) chartDataSet.drawValuesEnabled = true //是否显示气泡上的数字 //目前气泡图包括1组数据 let chartData = BubbleChartData(dataSets: [chartDataSet]) return chartData } //生成烛形图数据 func generateCandleData() -> CandleChartData { //生成10条随机数据 let dataEntries = (0..<10).map { (i) -> CandleChartDataEntry in let val = Double(arc4random_uniform(100) + 10 + 400) let high = Double(arc4random_uniform(20) + 8) let low = Double(arc4random_uniform(20) + 8) let open = Double(arc4random_uniform(20) + 1) let close = Double(arc4random_uniform(20) + 1) let even = arc4random_uniform(2) % 2 == 0 //true表示开盘价高于收盘价 return CandleChartDataEntry(x: Double(i), shadowH: val + high, shadowL: val - low, open: even ? val + open : val - open, close: even ? val - close : val + close) } let chartDataSet = CandleChartDataSet(values: dataEntries, label: "烛形图") chartDataSet.setColor(ChartColorTemplates.material()[2]) chartDataSet.shadowColor = .darkGray //柱线(烛心线)颜色 chartDataSet.decreasingColor = ZHFColor.green //实心颜色 chartDataSet.increasingColor = ZHFColor.red //空心颜色 //目前烛形图包括1组数据 let chartData = CandleChartData(dataSets: [chartDataSet]) return chartData } } //MARK:- ChartViewDelegate extension CombinedChartVC: ChartViewDelegate,IAxisValueFormatter { func stringForValue(_ value: Double, axis: AxisBase?) -> String { return months[Int(value) % months.count] } //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: ChartsUnderstandAndUsage/LineChartVC.swift ================================================ // // LineChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/17. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*折线图*/ import UIKit import Charts class LineChartVC: BaseVC { var circleColors :[UIColor] = [UIColor]() var lineChartView: LineChartView = LineChartView() override func viewDidLoad() { super.viewDidLoad() //添加折线 addLineChart() //折线图描述文字和样式 chartDescription() //设置交互样式 interactionStyle() //修改背景色和边框样式 setBackgroundBorder() //设置x轴的样式属性 setXAxisStyle() //设置y轴的样式属性 setYAxisStyle() //设置限制线(可设置多根) setlimitLine() //添加(刷新数据) updataData() } } //MARK:- UI和折线图基本样式 extension LineChartVC{ //添加折线 func addLineChart(){ lineChartView.backgroundColor = ZHFColor.white lineChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) lineChartView.center = self.view.center lineChartView.delegate = self self.view.addSubview(lineChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } //设置交互样式 func interactionStyle(){ lineChartView.scaleYEnabled = false //取消Y轴缩放 lineChartView.doubleTapToZoomEnabled = true //双击缩放 lineChartView.dragEnabled = true //启用拖动手势 lineChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果 lineChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果摩擦系数(0~1)越小惯性越不明显 } //描述文字 func chartDescription(){ lineChartView.noDataText = "暂无数据" //如果没有数据会显示这个 lineChartView.chartDescription?.text = "折线图描述" lineChartView.chartDescription?.position = CGPoint.init(x: lineChartView.frame.width - 30, y:lineChartView.frame.height - 20)//位置(及在lineChartView的中心点) lineChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 lineChartView.chartDescription?.textColor = UIColor.red lineChartView.legend.textColor = ZHFColor.purple //描述文字颜色 lineChartView.legend.formSize = 10 //(图例大小)默认是8 lineChartView.legend.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) } //修改背景色和边框样式 func setBackgroundBorder(){ // lineChartView.drawGridBackgroundEnabled = true //绘制图形区域背景 // lineChartView.gridBackgroundColor = ZHFColor.yellow //背景改成黄色(默认为浅灰色) lineChartView.drawBordersEnabled = true //绘制图形区域边框 lineChartView.borderColor = ZHFColor.red //边框为红色 lineChartView.borderLineWidth = 2 //边框线条大小为2 } //设置x轴的样式属性 func setXAxisStyle(){ //轴线宽、颜色、刻度、间隔 lineChartView.xAxis.axisLineWidth = 2 //x轴宽度 lineChartView.xAxis.axisLineColor = .black //x轴颜色 lineChartView.xAxis.axisMinimum = 0 //最小刻度值 lineChartView.xAxis.axisMaximum = 10 //最大刻度值 lineChartView.xAxis.granularity = 1 //最小间隔 //文字属性 lineChartView.xAxis.labelPosition = .bottom //x轴上的数字显示在下方(默认显示在上方 .top .bottom .bothSided .topInside .bottomInside) lineChartView.xAxis.labelTextColor = .red //刻度文字颜色 lineChartView.xAxis.labelFont = .systemFont(ofSize: 13) //刻度文字大小 lineChartView.xAxis.labelRotationAngle = -20 //刻度文字倾斜角度 //文字格式 let formatter = NumberFormatter() //自定义格式 formatter.positivePrefix = "#" //数字前缀positivePrefix、 后缀positiveSuffix lineChartView.xAxis.valueFormatter = DefaultAxisValueFormatter(formatter: formatter) //自定义刻度标签文字 // let xValues = ["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月"] // lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: xValues) //网格线 lineChartView.xAxis.drawGridLinesEnabled = true //制网格线 lineChartView.xAxis.gridColor = .orange //x轴对应网格线的颜色 lineChartView.xAxis.gridLineWidth = 2 //x轴对应网格线的大小 lineChartView.xAxis.gridLineDashLengths = [4,2] //虚线各段长度 } //设置y轴的样式属性(分左、右侧) func setYAxisStyle(){ //右侧(默认显示) // lineChartView.rightAxis.drawLabelsEnabled = false //不绘制右侧Y轴文字 // lineChartView.rightAxis.drawAxisLineEnabled = false //不显示右侧Y轴 // lineChartView.rightAxis.enabled = false //禁用右侧的Y轴 //左侧 // lineChartView.leftAxis.inverted = true //刻度值反向排列(默认正向) // lineChartView.leftAxis.labelPosition = .insideChart //文字显示在内侧 //0刻度线 lineChartView.leftAxis.drawZeroLineEnabled = true //绘制0刻度线 lineChartView.leftAxis.zeroLineColor = .red //0刻度线颜色 lineChartView.leftAxis.zeroLineWidth = 2 //0刻度线线宽 lineChartView.leftAxis.zeroLineDashLengths = [4, 2] //0刻度线使用虚线样式 //(1.轴线宽、颜色、刻度、间隔 2.文字属性 3.文字格式、4.网格线)和 func setXAxisStyle()方法一样 } //设置限制线(可设置多根) func setlimitLine(){ //界限1 let limitLine1 = ChartLimitLine(limit: 85, label: "优秀") limitLine1.lineColor = ZHFColor.green limitLine1.lineWidth = 2 //线宽 limitLine1.lineDashLengths = [4, 2] //虚线样式 //limitLine1.drawLabelEnabled = false //不绘制文字 limitLine1.valueTextColor = UIColor.blue //文字颜色 limitLine1.valueFont = UIFont.systemFont(ofSize: 13) //文字大小 limitLine1.labelPosition = .leftTop //文字位置 /*.leftTop:左上 .leftBottom:左下 .rightTop:右上(默认) .rightBottom:右下 */ lineChartView.leftAxis.addLimitLine(limitLine1) //界限2 let limitLine2 = ChartLimitLine(limit: 60, label: "合格") limitLine1.lineColor = ZHFColor.purple lineChartView.leftAxis.addLimitLine(limitLine2) lineChartView.leftAxis.drawLimitLinesBehindDataEnabled = true//将限制线绘制在折线后面 } } //MARK:- 数据加载和刷新 extension LineChartVC{ @objc func updataData(){ //1.第一条折线 //生成20条随机数据 var dataEntries1 = [ChartDataEntry]() for i in 0..<20 { let y = 50 - arc4random()%50 //或者 arc4random_uniform(UInt32(100)) let entry = ChartDataEntry.init(x: Double(i), y: Double(y)) dataEntries1.append(entry) circleColors.append(ZHFColor.blue) } //设置折线 let chartDataSet1 = LineChartDataSet(values: dataEntries1, label: "张三") chartDataSet1.setColors(ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置折线颜色(是一个循环,例如:你设置5个颜色,你设置8条折线,后三个对应的颜色是该设置中的前三个,依次类推) // chartDataSet1.setColors(ChartColorTemplates.material(), alpha: 1) //chartDataSet1.setColor(ZHFColor.gray)//颜色一致 chartDataSet1.lineWidth = 3 //线条宽度 chartDataSet1.lineDashLengths = [4,2] //设置折线为虚线各段长度 chartDataSet1.mode = .horizontalBezier //贝塞尔曲线(默认是折线 .linear .stepped .cubicBezier .horizontalBezier) //设置折点 // chartDataSet1.drawCirclesEnabled = false //不绘制转折点 // chartDataSet1.drawCircleHoleEnabled = false //不绘制转折点内圆 chartDataSet1.circleColors = circleColors //外圆颜色 chartDataSet1.circleHoleColor = ZHFColor.yellow //内圆颜色 chartDataSet1.circleRadius = 6 //外圆半径 chartDataSet1.circleHoleRadius = 4 //内圆半径 //设置折线上的文字 chartDataSet1.drawValuesEnabled = true //绘制拐点上的文字(默认绘制) chartDataSet1.valueColors = [.blue] //拐点上的文字颜色 chartDataSet1.valueFont = .systemFont(ofSize: 9) //拐点上的文字大小 //文字格式 let formatter = NumberFormatter() //自定义格式 formatter.positiveSuffix = "%" //数字后缀单位 chartDataSet1.valueFormatter = DefaultValueFormatter(formatter: formatter) //绘制填充色背景 //*半透明的填充色 chartDataSet1.drawFilledEnabled = true //开启填充色绘制 chartDataSet1.fillColor = .orange //设置填充色 chartDataSet1.fillAlpha = 0.5 //设置填充色透明度 //*渐变色填充 //开启填充色绘制 chartDataSet1.drawFilledEnabled = true //渐变颜色数组 let gradientColors = [UIColor.orange.cgColor, UIColor.white.cgColor] as CFArray //每组颜色所在位置(范围0~1) let colorLocations:[CGFloat] = [1.0, 0.0] //生成渐变色 let gradient = CGGradient.init(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: gradientColors, locations: colorLocations) //将渐变色作为填充对象s chartDataSet1.fill = Fill.fillWithLinearGradient(gradient!, angle: 90.0) //1.第二条折线 var dataEntries2 = [ChartDataEntry]() for i in 0..<20 { let y = 50 + arc4random()%50 //或者 arc4random_uniform(UInt32(100)) let entry = ChartDataEntry.init(x: Double(i), y: Double(y)) dataEntries2.append(entry) } let chartDataSet2 = LineChartDataSet(values: dataEntries2, label: "李四") //chartDataSet2.setColors(ZHFColor.gray,ZHFColor.green,ZHFColor.yellow,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置折线颜色(是一个循环,例如:你设置5个颜色,你设置8条折线,后三个对应的颜色是该设置中的前三个,依次类推) // chartDataSet2.setColors(ChartColorTemplates.material(), alpha: 1) chartDataSet2.setColor(ZHFColor.gray)//颜色一致 chartDataSet2.lineWidth = 2 let chartData = LineChartData(dataSets: [chartDataSet1,chartDataSet2]) //设置折现图数据 lineChartView.data = chartData lineChartView.animate(xAxisDuration: 2)//展示方式xAxisDuration 和 yAxisDuration两种 } } //MARK:- ChartViewDelegate extension LineChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") //将选中的数据点的颜色改成黄色 var chartDataSet = LineChartDataSet() chartDataSet = (chartView.data?.dataSets[0] as? LineChartDataSet)! let values = chartDataSet.values let index = values.index(where: {$0.x == highlight.x}) //获取索引 chartDataSet.circleColors = circleColors //还原 chartDataSet.circleColors[index!] = .orange //重新渲染表格 chartView.data?.notifyDataChanged() chartView.notifyDataSetChanged() // //显示该点的MarkerView标签(不同形式) // self.showMarkerView(value: "\(entry.y)") self.showBalloonMarkerView(value: "\(entry.y)") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "取消选中") //还原所有点的颜色 var chartDataSet = LineChartDataSet() chartDataSet = (chartView.data?.dataSets[0] as? LineChartDataSet)! chartDataSet.circleColors = circleColors //重新渲染表格 chartView.data?.notifyDataChanged() chartView.notifyDataSetChanged() } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } //MARK:- MarkerView标签 extension LineChartVC{ //显示MarkerView标签 func showMarkerView(value:String){ let marker = MarkerView(frame: CGRect(x: 20, y: 20, width: 80, height: 20)) marker.chartView = self.lineChartView let label = UILabel(frame: CGRect(x: 0, y: 0, width: 80, height: 20)) label.text = "数据:\(value)" label.textColor = UIColor.white label.font = UIFont.systemFont(ofSize: 12) label.backgroundColor = UIColor.gray label.textAlignment = .center marker.addSubview(label) self.lineChartView.marker = marker } //显示BalloonMarkerView标签 func showBalloonMarkerView(value:String){ let marker = BalloonMarker(color: UIColor(white: 180/255, alpha: 1), font: .systemFont(ofSize: 12), textColor: .white, insets: UIEdgeInsets(top: 8, left: 8, bottom: 20, right: 8)) marker.chartView = self.lineChartView marker.chartView = self.lineChartView marker.minimumSize = CGSize(width: 80, height: 40) marker.setLabel("数据:\(value)") self.lineChartView.marker = marker } } ================================================ FILE: ChartsUnderstandAndUsage/LineFilledChartVC.swift ================================================ // // LineFilledChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/18. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*折线填充图*/ import UIKit import Charts class LineFilledChartVC: BaseVC { var lineChartView: LineChartView = LineChartView() override func viewDidLoad() { super.viewDidLoad() //添加折线 addLineChart() //设置基本样式 setLineChartViewBaseStyle() //添加(刷新数据) updataData() } //添加折线 func addLineChart(){ lineChartView.backgroundColor = ZHFColor.white lineChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) lineChartView.center = self.view.center lineChartView.delegate = self self.view.addSubview(lineChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } //基本样式 func setLineChartViewBaseStyle(){ lineChartView.noDataText = "暂无数据" //如果没有数据会显示这个 lineChartView.drawGridBackgroundEnabled = true //绘制图形区域背景 lineChartView.gridBackgroundColor = ZHFColor.blue //背景颜色 lineChartView.alpha = 0.5//背景透明度 //折线图描述文字和样式 lineChartView.chartDescription?.text = "折线图描述" lineChartView.legend.textColor = ZHFColor.purple //描述文字颜色 lineChartView.legend.formSize = 10 //(图例大小)默认是8 lineChartView.legend.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) lineChartView.chartDescription?.position = CGPoint.init(x: lineChartView.frame.width - 30, y:lineChartView.frame.height - 20)//位置(及在lineChartView的中心点) lineChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 lineChartView.chartDescription?.textColor = UIColor.red //设置交互样式 lineChartView.scaleYEnabled = false //取消Y轴缩放 lineChartView.doubleTapToZoomEnabled = true //双击缩放 lineChartView.dragEnabled = true //启用拖动手势 lineChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果 lineChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果摩擦系数(0~1)越小惯性越不明显 //修改背景色和边框样式 lineChartView.drawBordersEnabled = true //绘制图形区域边框 lineChartView.borderColor = ZHFColor.red //边框为红色 lineChartView.borderLineWidth = 2 //边框线条大小为2 } @objc func updataData(){ //1.第一条折线 //生成30条随机数据 var dataEntries1 = [ChartDataEntry]() for i in 0..<30 { let y = arc4random()%10 + 20 //或者 arc4random_uniform(UInt32(100)) let entry = ChartDataEntry.init(x: Double(i), y: Double(y)) dataEntries1.append(entry) } let chartDataSet1 = LineChartDataSet(values: dataEntries1, label: "上折线") //chartDataSet1.setColors(ZHFColor.gray,ZHFColor.green,ZHFColor.yellow,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置折线颜色(是一个循环,例如:你设置5个颜色,你设置8条折线,后三个对应的颜色是该设置中的前三个,依次类推) // chartDataSet1.setColors(ChartColorTemplates.material(), alpha: 1) chartDataSet1.setColor(ZHFColor.gray)//颜色一致 chartDataSet1.lineWidth = 2 chartDataSet1.drawCirclesEnabled = false //不绘制拐点 chartDataSet1.fillAlpha = 1 chartDataSet1.drawFilledEnabled = true //绘制上填充色 chartDataSet1.fillColor = .white chartDataSet1.drawValuesEnabled = false //不绘制拐点上的文字 chartDataSet1.fillFormatter = DefaultFillFormatter { _,_ -> CGFloat in return CGFloat(self.lineChartView.leftAxis.axisMaximum) //向上绘制填充区域 } //修改点击时十字线的样式 chartDataSet1.highlightColor = .red //十字线颜色 chartDataSet1.highlightLineWidth = 2 //十字线线宽 chartDataSet1.highlightLineDashLengths = [4, 2] //使用虚线样式的十字线 // chartDataSet1.highlightEnabled = false //不启用十字线 chartDataSet1.drawVerticalHighlightIndicatorEnabled = false //不显示纵向十字线 // chartDataSet1.drawHorizontalHighlightIndicatorEnabled = false //不显示横向十字线 //2.第二条折线 var dataEntries2 = [ChartDataEntry]() for i in 0..<30 { let y = arc4random()%10 //或者 arc4random_uniform(UInt32(100)) let entry = ChartDataEntry.init(x: Double(i), y: Double(y)) dataEntries2.append(entry) } let chartDataSet2 = LineChartDataSet(values: dataEntries2, label: "下折线") //chartDataSet2.setColors(ZHFColor.gray,ZHFColor.green,ZHFColor.yellow,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置折线颜色(是一个循环,例如:你设置5个颜色,你设置8条折线,后三个对应的颜色是该设置中的前三个,依次类推) // chartDataSet2.setColors(ChartColorTemplates.material(), alpha: 1) chartDataSet2.setColor(ZHFColor.green)//颜色一致 chartDataSet2.lineWidth = 2 chartDataSet2.drawCirclesEnabled = false //不绘制拐点 chartDataSet2.fillAlpha = 1 chartDataSet2.drawFilledEnabled = true //绘制下填充色 chartDataSet2.fillColor = .white chartDataSet2.drawValuesEnabled = false //不绘制拐点上的文字 chartDataSet2.fillFormatter = DefaultFillFormatter { _,_ -> CGFloat in return CGFloat(self.lineChartView.leftAxis.axisMinimum) //向下绘制填充区域 } //修改点击时十字线的样式 chartDataSet2.highlightColor = .red //十字线颜色 chartDataSet2.highlightLineWidth = 2 //十字线线宽 chartDataSet2.highlightLineDashLengths = [4, 2] //使用虚线样式的十字线 let chartData = LineChartData(dataSets: [chartDataSet1,chartDataSet2]) //设置折现图数据 lineChartView.data = chartData } } extension LineFilledChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/PieChartHalfVC.swift ================================================ // // PieChartHalfVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/19. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*半个饼状图*/ import UIKit import Charts class PieChartHalfVC: BaseVC { var pieChartView: PieChartView = PieChartView() var data: PieChartData = PieChartData() override func viewDidLoad() { super.viewDidLoad() //1.添加饼状图 addPieChart() //2.设置基本样式 setPieChartViewBaseStyle() //3.添加(刷新数据) updataData() } //添加饼状图 func addPieChart(){ pieChartView.backgroundColor = ZHFColor.white pieChartView.frame.size = CGSize.init(width: ScreenWidth, height: 300) pieChartView.center = self.view.center pieChartView.delegate = self self.view.addSubview(pieChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setPieChartViewBaseStyle(){ //基本样式 pieChartView.setExtraOffsets(left: 30, top: 30, right: 30, bottom: 0)//饼状图距离边缘的间隙 pieChartView.usePercentValuesEnabled = true//是否根据所提供的数据, 将显示数据转换为百分比格式 pieChartView.dragDecelerationEnabled = true//拖拽饼状图后是否有惯性效果 pieChartView.drawSlicesUnderHoleEnabled = true//是否显示区块文本 //空(实)心饼状图样式 pieChartView.drawHoleEnabled = true//饼状图是否是空心 true为空心 false为实心 pieChartView.holeRadiusPercent = 0.5//空心半径占比 pieChartView.holeColor = ZHFColor.white//空心颜色 这个不能设置成clear pieChartView.transparentCircleRadiusPercent = 0.54//半透明空心半径占比 pieChartView.transparentCircleColor = ZHFColor.zhf_colorAlpha(withHex: 0xffffff, alpha: 0.4)//半透明空心的颜色 //设置成半圆 pieChartView.maxAngle = 180 // Half chart pieChartView.rotationAngle = 180 // Rotate to make the half on the upper side pieChartView.centerTextOffset = CGPoint(x: 0, y: -20)//上移20 //饼状图中间描述 if pieChartView.isDrawHoleEnabled == true { pieChartView.drawCenterTextEnabled = true //pieChartView.centerText = "饼状图" //富文本 let centerText : NSMutableAttributedString = NSMutableAttributedString.init(string: "半圆饼状图") centerText.setAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15),NSAttributedString.Key.foregroundColor: ZHFColor.green], range: NSRange.init(location: 0, length: 2)) pieChartView.centerAttributedText = centerText } else{} //饼状图描述 pieChartView.chartDescription?.text = "饼状图示例" pieChartView.chartDescription?.font = UIFont.systemFont(ofSize: 10) pieChartView.chartDescription?.textColor = ZHFColor.zhf33_titleTextColor //饼状图图例 let l = pieChartView.legend l.maxSizePercent = 1 //图例在饼状图中的大小占比, 这会影响图例的宽高 l.formToTextSpace = 5 //文本间隔 l.font = UIFont.systemFont(ofSize: 10)//字体大小 l.textColor = ZHFColor.gray//字体颜色 l.form = Legend.Form.circle//图示样式: 方形、线条、圆形 //图例在饼状图中的位置(右上角) l.horizontalAlignment = Legend.HorizontalAlignment.right l.verticalAlignment = Legend.VerticalAlignment.top l.orientation = Legend.Orientation.vertical l.formSize = 12;//图示大小 } @objc func updataData(){ //对应x轴上面需要显示的数据 let count = 5 //对应Y轴上面需要显示的数据 let yVals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { let val: Double = Double(arc4random_uniform(UInt32(200))) let entry:PieChartDataEntry = PieChartDataEntry.init(value: val, label: "paty\(i)") // let entry:BarChartDataEntry = BarChartDataEntry.init(x: Double(i), y: Double(val)) yVals.add(entry) } //创建PieChartDataSet对象 let set1: PieChartDataSet = PieChartDataSet.init(values: yVals as? [ChartDataEntry], label: "饼状图") set1.drawIconsEnabled = false //是否在饼状图上面显示图片 set1.sliceSpace = 2 //相邻区块之间的间距 set1.selectionShift = 8//选中区块时, 放大的半径 set1.drawValuesEnabled = true //是否在饼状图上面显示数值 set1.highlightEnabled = true //点击选饼状图是否有高亮效果,(单击空白处取消选中) set1.setColors(ZHFColor.gray,ZHFColor.blue,ZHFColor.red,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置柱形图颜色(是一个循环,例如:你设置5个颜色,你设置8个柱形,后三个对应的颜色是该设置中的前三个,依次类推) // set1.setColors(ChartColorTemplates.material(), alpha: 1) // set1.setColor(ZHFColor.gray)//颜色一致 set1.xValuePosition = PieChartDataSet.ValuePosition.insideSlice//名称位置(名称显示在饼图内部) //外部条件下有折线 set1.yValuePosition = PieChartDataSet.ValuePosition.insideSlice//数据位置 //数据与区块之间的用于指示的折线样式 set1.valueLinePart1OffsetPercentage = 0.85//折线中第一段起始位置相对于区块的偏移量, 数值越大, 折线距离区块越远 set1.valueLinePart1Length = 0.5//折线中第一段长度占比 set1.valueLinePart2Length = 0.4//折线中第二段长度最大占比 set1.valueLineWidth = 1//折线的粗细 set1.valueLineColor = ZHFColor.brown//折线颜色 let dataSets: NSMutableArray = NSMutableArray.init() dataSets.add(set1) //创建BarChartData对象, 此对象就是barChartView需要最终数据对象 let data: PieChartData = PieChartData.init(dataSets: dataSets as? [IChartDataSet]) let formatter: NumberFormatter = NumberFormatter.init() formatter.numberStyle = NumberFormatter.Style.percent //自定义数据显示格式 小数点形式(可以尝试不同看效果) formatter.maximumFractionDigits = 1 formatter.multiplier = 1 formatter.percentSymbol = " %" let forma :DefaultValueFormatter = DefaultValueFormatter.init(formatter: formatter) data.setValueFormatter(forma) data.setValueFont(UIFont.systemFont(ofSize: 10)) data.setValueTextColor(ZHFColor.orange) pieChartView.data = data pieChartView.animate(xAxisDuration: 1, easingOption: ChartEasingOption.easeOutExpo) } } //MARK:- extension PieChartHalfVC :ChartViewDelegate { //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/PieChartPolylineVC.swift ================================================ // // PieChartPolylineVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/10/18. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*饼状图(折线注释)*/ import UIKit import Charts class PieChartPolylineVC: BaseVC { var pieChartView: PieChartView = PieChartView() var data: PieChartData = PieChartData() override func viewDidLoad() { super.viewDidLoad() //添加饼状图 addPieChart() //设置基本样式 setPieChartViewBaseStyle() //3.添加(刷新数据) updataData() } //添加饼状图 func addPieChart(){ pieChartView.backgroundColor = ZHFColor.white pieChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) pieChartView.center = self.view.center pieChartView.delegate = self self.view.addSubview(pieChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setPieChartViewBaseStyle(){ //基本样式 pieChartView.setExtraOffsets(left: 30, top: 30, right: 30, bottom: 0)//饼状图距离边缘的间隙 pieChartView.usePercentValuesEnabled = true//是否根据所提供的数据, 将显示数据转换为百分比格式 pieChartView.dragDecelerationEnabled = true//拖拽饼状图后是否有惯性效果 pieChartView.drawSlicesUnderHoleEnabled = true//是否显示区块文本 //空(实)心饼状图样式 pieChartView.drawHoleEnabled = true//饼状图是否是空心 true为空心 false为实心 pieChartView.holeRadiusPercent = 0.5//空心半径占比 pieChartView.holeColor = ZHFColor.white//空心颜色 这个不能设置成clear pieChartView.transparentCircleRadiusPercent = 0.54//半透明空心半径占比 pieChartView.transparentCircleColor = ZHFColor.zhf_colorAlpha(withHex: 0xffffff, alpha: 0.4)//半透明空心的颜色 //饼状图中间描述 if pieChartView.isDrawHoleEnabled == true { pieChartView.drawCenterTextEnabled = true pieChartView.centerText = "饼状图" //富文本 // let centerText : NSMutableAttributedString = NSMutableAttributedString.init(string: "饼状图") // centerText.setAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15),NSAttributedString.Key.foregroundColor: ZHFColor.green], range: NSRange.init(location: 0, length: centerText.length)) // pieChartView.centerAttributedText = centerText } else{} //饼状图描述 pieChartView.chartDescription?.text = "饼状图示例" pieChartView.chartDescription?.font = UIFont.systemFont(ofSize: 10) pieChartView.chartDescription?.textColor = ZHFColor.zhf33_titleTextColor //饼状图图例 let l = pieChartView.legend l.maxSizePercent = 1 //图例在饼状图中的大小占比, 这会影响图例的宽高 l.formToTextSpace = 5 //文本间隔 l.font = UIFont.systemFont(ofSize: 10)//字体大小 l.textColor = ZHFColor.gray//字体颜色 l.form = Legend.Form.circle//图示样式: 方形、线条、圆形 //图例在饼状图中的位置(上局中、 水平布局) l.horizontalAlignment = Legend.HorizontalAlignment.center l.verticalAlignment = Legend.VerticalAlignment.top l.orientation = Legend.Orientation.horizontal //水平布局 l.formSize = 12;//图示大小 } @objc func updataData(){ //对应x轴上面需要显示的数据 let count = 5 //对应Y轴上面需要显示的数据 let yVals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { let val: Double = Double(arc4random_uniform(UInt32(200))) let entry:PieChartDataEntry = PieChartDataEntry.init(value: val, label: "paty\(i)") // let entry:BarChartDataEntry = BarChartDataEntry.init(x: Double(i), y: Double(val)) yVals.add(entry) } //创建PieChartDataSet对象 let set1: PieChartDataSet = PieChartDataSet.init(values: yVals as? [ChartDataEntry], label: "饼状图") set1.drawIconsEnabled = false //是否在饼状图上面显示图片 set1.sliceSpace = 2 //相邻区块之间的间距 set1.selectionShift = 8//选中区块时, 放大的半径 set1.drawValuesEnabled = true //是否在饼状图上面显示数值 set1.highlightEnabled = true //点击选饼状图是否有高亮效果,(单击空白处取消选中) set1.setColors(ZHFColor.gray,ZHFColor.blue,ZHFColor.red,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置柱形图颜色(是一个循环,例如:你设置5个颜色,你设置8个柱形,后三个对应的颜色是该设置中的前三个,依次类推) // set1.setColors(ChartColorTemplates.material(), alpha: 1) // set1.setColor(ZHFColor.gray)//颜色一致 set1.xValuePosition = PieChartDataSet.ValuePosition.insideSlice//名称位置 //PieChartDataSet.ValuePosition.insideSlice 数据显示在饼图内部 PieChartDataSet.ValuePosition.outsideSlice外部 //外部条件下有折线 set1.yValuePosition = PieChartDataSet.ValuePosition.insideSlice//数据位置 //数据与区块之间的用于指示的折线样式 set1.valueLinePart1OffsetPercentage = 0.85//折线中第一段起始位置相对于区块的偏移量, 数值越大, 折线距离区块越远 set1.valueLinePart1Length = 0.5//折线中第一段长度占比 set1.valueLinePart2Length = 0.4//折线中第二段长度最大占比 set1.valueLineWidth = 1//折线的粗细 set1.yValuePosition = .outsideSlice //这个折线外部展示 set1.valueLineColor = ZHFColor.red//折线颜色 let dataSets: NSMutableArray = NSMutableArray.init() dataSets.add(set1) //创建BarChartData对象, 此对象就是barChartView需要最终数据对象 let data: PieChartData = PieChartData.init(dataSets: dataSets as? [IChartDataSet]) let formatter: NumberFormatter = NumberFormatter.init() //formatter.numberStyle = NumberFormatter.Style.currency//自定义数据显示格式 小数点形式(可以尝试不同看效果) formatter.numberStyle = NumberFormatter.Style.percent //自定义数据显示格式 小数点形式(可以尝试不同看效果) formatter.maximumFractionDigits = 0 formatter.multiplier = 1 let forma :DefaultValueFormatter = DefaultValueFormatter.init(formatter: formatter) data.setValueFormatter(forma) data.setValueFont(UIFont.systemFont(ofSize: 10)) data.setValueTextColor(ZHFColor.orange) pieChartView.data = data pieChartView.animate(xAxisDuration: 1, easingOption: ChartEasingOption.easeOutExpo) } } //MARK:- extension PieChartPolylineVC :ChartViewDelegate { //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/PieChartVC.swift ================================================ // // PieChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/13. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*饼状图*/ import UIKit import Charts class PieChartVC: BaseVC { var pieChartView: PieChartView = PieChartView() var data: PieChartData = PieChartData() override func viewDidLoad() { super.viewDidLoad() //添加饼状图 addPieChart() //设置基本样式 setPieChartViewBaseStyle() //3.添加(刷新数据) updataData() } //添加饼状图 func addPieChart(){ pieChartView.backgroundColor = ZHFColor.white pieChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) pieChartView.center = self.view.center pieChartView.delegate = self self.view.addSubview(pieChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setPieChartViewBaseStyle(){ //基本样式 pieChartView.setExtraOffsets(left: 30, top: 30, right: 30, bottom: 0)//饼状图距离边缘的间隙 pieChartView.usePercentValuesEnabled = true//是否根据所提供的数据, 将显示数据转换为百分比格式 pieChartView.dragDecelerationEnabled = true//拖拽饼状图后是否有惯性效果 pieChartView.drawSlicesUnderHoleEnabled = true//是否显示区块文本 //空(实)心饼状图样式 pieChartView.drawHoleEnabled = true//饼状图是否是空心 true为空心 false为实心 pieChartView.holeRadiusPercent = 0.5//空心半径占比 pieChartView.holeColor = ZHFColor.white//空心颜色 这个不能设置成clear pieChartView.transparentCircleRadiusPercent = 0.54//半透明空心半径占比 pieChartView.transparentCircleColor = ZHFColor.zhf_colorAlpha(withHex: 0xffffff, alpha: 0.4)//半透明空心的颜色 //饼状图中间描述 if pieChartView.isDrawHoleEnabled == true { pieChartView.drawCenterTextEnabled = true pieChartView.centerText = "饼状图" //富文本 // let centerText : NSMutableAttributedString = NSMutableAttributedString.init(string: "饼状图") // centerText.setAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15),NSAttributedString.Key.foregroundColor: ZHFColor.green], range: NSRange.init(location: 0, length: centerText.length)) // pieChartView.centerAttributedText = centerText } else{} //饼状图描述 pieChartView.chartDescription?.text = "饼状图示例" pieChartView.chartDescription?.font = UIFont.systemFont(ofSize: 10) pieChartView.chartDescription?.textColor = ZHFColor.zhf33_titleTextColor //饼状图图例 let l = pieChartView.legend l.maxSizePercent = 1 //图例在饼状图中的大小占比, 这会影响图例的宽高 l.formToTextSpace = 5 //文本间隔 l.font = UIFont.systemFont(ofSize: 10)//字体大小 l.textColor = ZHFColor.gray//字体颜色 l.form = Legend.Form.circle//图示样式: 方形、线条、圆形 //图例在饼状图中的位置(上局中、 水平布局) l.horizontalAlignment = Legend.HorizontalAlignment.center l.verticalAlignment = Legend.VerticalAlignment.top l.orientation = Legend.Orientation.horizontal //水平布局 l.formSize = 12;//图示大小 } @objc func updataData(){ //对应x轴上面需要显示的数据 let count = 5 //对应Y轴上面需要显示的数据 let yVals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { let val: Double = Double(arc4random_uniform(UInt32(200))) let entry:PieChartDataEntry = PieChartDataEntry.init(value: val, label: "paty\(i)") // let entry:BarChartDataEntry = BarChartDataEntry.init(x: Double(i), y: Double(val)) yVals.add(entry) } //创建PieChartDataSet对象 let set1: PieChartDataSet = PieChartDataSet.init(values: yVals as? [ChartDataEntry], label: "饼状图") set1.drawIconsEnabled = false //是否在饼状图上面显示图片 set1.sliceSpace = 2 //相邻区块之间的间距 set1.selectionShift = 8//选中区块时, 放大的半径 set1.drawValuesEnabled = true //是否在饼状图上面显示数值 set1.highlightEnabled = true //点击选饼状图是否有高亮效果,(单击空白处取消选中) set1.setColors(ZHFColor.gray,ZHFColor.blue,ZHFColor.red,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置柱形图颜色(是一个循环,例如:你设置5个颜色,你设置8个柱形,后三个对应的颜色是该设置中的前三个,依次类推) // set1.setColors(ChartColorTemplates.material(), alpha: 1) // set1.setColor(ZHFColor.gray)//颜色一致 set1.xValuePosition = PieChartDataSet.ValuePosition.insideSlice//名称位置 //PieChartDataSet.ValuePosition.insideSlice 数据显示在饼图内部 let dataSets: NSMutableArray = NSMutableArray.init() dataSets.add(set1) //创建BarChartData对象, 此对象就是barChartView需要最终数据对象 let data: PieChartData = PieChartData.init(dataSets: dataSets as? [IChartDataSet]) let formatter: NumberFormatter = NumberFormatter.init() //formatter.numberStyle = NumberFormatter.Style.currency//自定义数据显示格式 小数点形式(可以尝试不同看效果) formatter.numberStyle = NumberFormatter.Style.percent //自定义数据显示格式 小数点形式(可以尝试不同看效果) formatter.maximumFractionDigits = 0 formatter.multiplier = 1 let forma :DefaultValueFormatter = DefaultValueFormatter.init(formatter: formatter) data.setValueFormatter(forma) data.setValueFont(UIFont.systemFont(ofSize: 10)) data.setValueTextColor(ZHFColor.orange) pieChartView.data = data pieChartView.animate(xAxisDuration: 1, easingOption: ChartEasingOption.easeOutExpo) } } //MARK:- extension PieChartVC :ChartViewDelegate { //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/RadarChartVC.swift ================================================ // // RadarChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/13. // Copyright © 2018年 张海峰. All rights reserved. //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*雷达图*/ import UIKit import Charts class RadarChartVC: BaseVC { var radarChartView: RadarChartView = RadarChartView() var data: RadarChartData = RadarChartData() let axisMaximum :Double = 150 lazy var xVals: NSMutableArray = NSMutableArray.init() override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = ZHFColor.yellow //添加雷达图 addRadarChart() //设置基本样式 setRadarChartViewBaseStyle() } //添加雷达图 func addRadarChart(){ radarChartView.backgroundColor = ZHFColor.white radarChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) radarChartView.center = self.view.center radarChartView.delegate = self self.view.addSubview(radarChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setRadarChartViewBaseStyle(){ //雷达图描述 radarChartView.rotationEnabled = true //是否允许转动 radarChartView.highlightPerTapEnabled = true //是否能被选中 //雷达图线条样式 radarChartView.webLineWidth = 0.5 //主干线线宽 radarChartView.webColor = ZHFColor.black radarChartView.innerWebLineWidth = 0.375 //边线线宽 radarChartView.innerWebColor = ZHFColor.black radarChartView.webAlpha = 1 //透明度 //设置 xAx let xAxis: XAxis = radarChartView.xAxis xAxis.valueFormatter = self //重写代理方法 设置y轴数据 xAxis.labelPosition = XAxis.LabelPosition.topInside //X轴(5种位置显示,根据需求进行设置) xAxis.labelFont = UIFont.systemFont(ofSize: 10)//x轴数值字体大小 xAxis.labelTextColor = ZHFColor.brown//数值字体颜色 //设置 yAxis let yAxis: YAxis = radarChartView.yAxis yAxis.axisMinimum = 0 yAxis.axisMaximum = axisMaximum yAxis.drawLabelsEnabled = false yAxis.labelCount = 8 yAxis.labelFont = UIFont.systemFont(ofSize: 10)//x轴数值字体大小 xAxis.labelTextColor = ZHFColor.brown//数值字体颜色 //雷达图图例 radarChartView.chartDescription?.text = "雷达图示例" radarChartView.chartDescription?.font = UIFont.systemFont(ofSize: 10) radarChartView.chartDescription?.textColor = ZHFColor.zhf33_titleTextColor radarChartView.chartDescription?.position = CGPoint.init(x: 80, y: 5)//位置(及在radarChartView的中心点) //图例在雷达图中的位置(右上角) radarChartView.legend.horizontalAlignment = Legend.HorizontalAlignment.right radarChartView.legend.verticalAlignment = Legend.VerticalAlignment.top radarChartView.legend.orientation = Legend.Orientation.horizontal radarChartView.legend.formSize = 10;//图示大小 radarChartView.legend.maxSizePercent = 1 //图例在饼状图中的大小占比, 这会影响图例的宽高 radarChartView.legend.formToTextSpace = 5 //文本间隔 radarChartView.legend.font = UIFont.systemFont(ofSize: 10)//字体大小 radarChartView.legend.textColor = ZHFColor.gray//字体颜色 radarChartView.legend.form = Legend.Form.circle//图示样式: 方形、线条、圆形 //为雷达图提供数据 self.data = setData() radarChartView.data = self.data; //设置动画效果 radarChartView.animate(yAxisDuration: 1)//展示方式xAxisDuration 和 yAxisDuration两种 } func setData() -> RadarChartData{ let count = 12 //对应x轴上面需要显示的数据 let x1Vals: NSMutableArray = NSMutableArray.init() for i in 0 ..< count { //x轴字体展示 x1Vals.add("\(i + 1)月") self.xVals = x1Vals } //对应Y轴上面需要显示的数据 let yVals: NSMutableArray = NSMutableArray.init() for _ in 0 ..< count { let val: Double = Double(arc4random_uniform(UInt32(axisMaximum - 50)) + 50) let entry:RadarChartDataEntry = RadarChartDataEntry.init(value: val) yVals.add(entry) } //创建RadarChartDataSet对象,其中包含有Y轴数据信息 let set1: RadarChartDataSet = RadarChartDataSet.init(values: yVals as? [ChartDataEntry], label: "雷达星座运势图") set1.lineWidth = 0.5 //数据折线线宽 set1.setColor(ZHFColor.gray)//颜色 set1.drawFilledEnabled = true ////是否填充颜色 set1.fillColor = ZHFColor.green set1.fillAlpha = 0.3 set1.drawValuesEnabled = true //是否绘制显示数据 set1.highlightEnabled = true //点击选饼状图是否有高亮效果,(单击空白处取消选中) set1.valueFont = UIFont.systemFont(ofSize: 10) set1.valueTextColor = ZHFColor.gray let dataSets: NSMutableArray = NSMutableArray.init() dataSets.add(set1) //创建RadarChartData对象, 此对象就是radarChartView需要最终数据对象 let data: RadarChartData = RadarChartData.init(dataSets: dataSets as? [IChartDataSet]) return data } @objc func updataData(){ //重新设置基本样式 setRadarChartViewBaseStyle() } } //MARK:- extension RadarChartVC :ChartViewDelegate,IAxisValueFormatter { func stringForValue(_ value: Double, axis: AxisBase?) -> String { if Int(value) > self.xVals.count - 1 { return self.xVals[Int(value - 1)] as! String } else{ return self.xVals[Int(value)] as! String } } //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/ScatterChartVC.swift ================================================ // // ScatterChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/17. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*散点图*/ import UIKit import Charts class ScatterChartVC: BaseVC { var scatterChartView: ScatterChartView = ScatterChartView() override func viewDidLoad() { super.viewDidLoad() //1.添加散点图 addScatterChart() //2. 基本样式 setScatterChartViewBaseStyle() //3.添加(刷新数据) updataData() } } extension ScatterChartVC{ //添加散点图 func addScatterChart(){ scatterChartView.backgroundColor = ZHFColor.white scatterChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300) scatterChartView.center = self.view.center scatterChartView.delegate = self self.view.addSubview(scatterChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } func setScatterChartViewBaseStyle(){ //散点图描述 scatterChartView.chartDescription?.text = "散点图描述" scatterChartView.chartDescription?.position = CGPoint.init(x: scatterChartView.frame.width - 30, y:scatterChartView.frame.height - 20)//位置(及在scatterChartView的中心点) scatterChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 scatterChartView.chartDescription?.textColor = UIColor.red //图例 let l = scatterChartView.legend l.wordWrapEnabled = false //显示图例 l.horizontalAlignment = .left //居左 l.verticalAlignment = .bottom //放在底部 l.orientation = .horizontal //水平排布 l.drawInside = false // 图例在外 l.formSize = 10 //(图例大小)默认是8 l.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) //Y轴右侧线 let rightAxis = scatterChartView.rightAxis rightAxis.axisMinimum = 0 //Y轴左侧线 let leftAxis = scatterChartView.leftAxis leftAxis.axisMinimum = 0 //X轴 let xAxis = scatterChartView.xAxis xAxis.labelPosition = .bothSided //分布在两边外部 xAxis.axisMinimum = 0 //最小刻度值 xAxis.granularity = 1 //最小间隔 } @objc func updataData(){ //第一组散点图的10条随机数据 let dataEntries1 = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(20) ) return ChartDataEntry(x: Double(i), y: val) } let chartDataSet1 = ScatterChartDataSet(values: dataEntries1, label: "图例1") chartDataSet1.setScatterShape(.circle) //使用圆形散点(三角形:.triangle 十字:.cross 叉:.x 上箭头:.chevronUp 下箭头:.chevronDown) chartDataSet1.setColor(.yellow) chartDataSet1.scatterShapeSize = 10 //散点大小 chartDataSet1.scatterShapeHoleRadius = 2.5 //内点大小 chartDataSet1.scatterShapeHoleColor = .red //内点颜色 //第二组散点图的10条随机数据 let dataEntries2 = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(20) + 20) return ChartDataEntry(x: Double(i), y: val) } let chartDataSet2 = ScatterChartDataSet(values: dataEntries2, label: "图例2") chartDataSet2.setColor(.red) chartDataSet2.scatterShapeSize = 10 //散点大小 chartDataSet2.scatterShapeHoleRadius = 2.5 //内点大小 chartDataSet2.scatterShapeHoleColor = .yellow //内点颜色 //第三组散点图的10条随机数据 let dataEntries3 = (0..<10).map { (i) -> ChartDataEntry in let val = Double(arc4random_uniform(20) + 40) return ChartDataEntry(x: Double(i), y: val) } let chartDataSet3 = ScatterChartDataSet(values: dataEntries3, label: "图例3") chartDataSet3.setScatterShape(.x)//使用圆形散点(三角形:.triangle 十字:.cross 叉:.x 上箭头:.chevronUp 下箭头:.chevronDown) chartDataSet3.setColor(.green) //目前散点图包括2组数据 let chartData = ScatterChartData(dataSets: [chartDataSet1, chartDataSet2,chartDataSet3]) //设置散点图数据 scatterChartView.data = chartData } } extension ScatterChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/ViewController.swift ================================================ // // ViewController.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/9/12. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git import UIKit //设备物理尺寸 let ScreenHeight = UIScreen.main.bounds.size.height let ScreenWidth = UIScreen.main.bounds.size.width private struct ItemMessage{ let title: String let subtitle: String let imageName: String let `class` : AnyClass } class ViewController: UIViewController { var tableView:UITableView! private var ItemMessages = [ItemMessage.init(title: "柱状图", subtitle: "Bar Chart(A simple demonstration of the bar chart.)", imageName: "barChartImage", class: BarChartVC.self), ItemMessage.init(title: "柱状图(波浪图)", subtitle: "Bar Chart Wave(A simple demonstration of the bar chart.)", imageName: "barChartWaveImage", class: BarChartWaveVC.self), ItemMessage.init(title: "饼状图", subtitle: "Pie Chart(A simple demonstration of the pie chart.)", imageName: "pieChartImage", class: PieChartVC.self), ItemMessage.init(title: "饼状图(半圆形)", subtitle: "Half Pie Chart (A simple demonstration of the pie chart.)", imageName: "pieChartHalfImage", class: PieChartHalfVC.self), ItemMessage.init(title: "饼状图(折线注释)", subtitle: "Polyline Pie Chart(A simple demonstration of the pie chart with polyline notes.)", imageName: "pieChartPolylineImage", class: PieChartPolylineVC.self), ItemMessage.init(title: "雷达图", subtitle: "Radar Chart(Demonstrates the use of a spider-web like (net) chart.)", imageName: "radarChartImage", class: RadarChartVC.self), ItemMessage.init(title: "折线图", subtitle: "Line Chart(A simple demonstration of the linechart.)", imageName: "lineChartImage", class: LineChartVC.self), ItemMessage.init(title: "折线填充图", subtitle: "Line Filled Chart(This demonstrates how to fill an area between two LineDataSets.)", imageName: "lineFilledChartImage", class: LineFilledChartVC.self), ItemMessage.init(title: "散点图", subtitle: "Scatter Chart(A simple demonstration of the scatter chart.)", imageName: "scatterChartImage", class: ScatterChartVC.self), ItemMessage.init(title: "K 线图(烛形图)", subtitle: "CandleStick Chart(Demonstrates usage of the CandleStickChart.)", imageName: "candleStickChartImage", class: CandleStickChartVC.self), ItemMessage.init(title: "气泡图", subtitle: "Bubble Chart(A simple demonstration of the bubble chart.)", imageName: "bubbleChartImage", class: BubbleChartVC.self), ItemMessage.init(title: "组合图(混合图)", subtitle: "Combined Chart(Demonstrates how to create a combined chart (bar and line in this case).", imageName: "combinedChartImage", class: CombinedChartVC.self), ItemMessage.init(title: "波浪图", subtitle: "Waveform Chart(根据一组Hz数据,只给最高点画波浪)", imageName: "waveformChartImage", class: WaveformChartVC.self),] lazy var dataMarr:NSMutableArray = NSMutableArray() override func viewDidLoad() { super.viewDidLoad() self.title = "Charts框架理解与使用" self.addTableView() } func addTableView(){ tableView = UITableView.init(frame: CGRect.init(x: 0, y: 44, width: ScreenWidth, height: ScreenHeight - 44), style: .plain) self.view.addSubview(tableView) tableView.backgroundColor = ZHFColor.zhff9_backGroundColor tableView.separatorColor = ZHFColor.zhf_strColor(hex: "cccccc") tableView.separatorInset = UIEdgeInsets.init(top: 0, left: 25, bottom: 0, right: 0) tableView.delegate = self tableView.dataSource = self } } extension ViewController :UITableViewDataSource,UITableViewDelegate { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.ItemMessages.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let itemMessage = self.ItemMessages[indexPath.row] var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") if cell == nil { cell = UITableViewCell(style:.subtitle, reuseIdentifier: "Cell") } cell?.textLabel?.text = itemMessage.title cell?.textLabel?.textColor = ZHFColor.red cell?.detailTextLabel?.text = itemMessage.subtitle cell?.detailTextLabel?.textColor = ZHFColor.zhf66_contentTextColor cell?.detailTextLabel?.numberOfLines = 0 cell?.imageView?.image = UIImage.init(named: itemMessage.imageName) cell?.selectionStyle = .none return cell! } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let itemMessage = self.ItemMessages[indexPath.row] let vcClass = itemMessage.class as! UIViewController.Type let vc = vcClass.init() self.navigationController?.pushViewController(vc, animated: true) tableView.deselectRow(at: indexPath, animated: true) } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 150 } } ================================================ FILE: ChartsUnderstandAndUsage/WaveformChartVC.swift ================================================ // // WaveformChartVC.swift // ChartsUnderstandAndUsage // // Created by 张海峰 on 2018/11/15. // Copyright © 2018年 张海峰. All rights reserved. ////Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git /*波浪图*/ import UIKit import Charts class WaveformChartVC: BaseVC { var lineChartView: LineChartView = LineChartView() override func viewDidLoad() { super.viewDidLoad() //添加折线 addLineChart() //设置基本样式 setLineChartViewBaseStyle() setXAxisStyle() setYAxisStyle() //添加(刷新数据) updataData() } //添加折线 func addLineChart(){ lineChartView.backgroundColor = ZHFColor.white lineChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 150) lineChartView.center = self.view.center lineChartView.delegate = self self.view.addSubview(lineChartView) //刷新按钮响应 refreshrBtn.addTarget(self, action: #selector(updataData), for: .touchUpInside) } //基本样式 func setLineChartViewBaseStyle(){ lineChartView.noDataText = "暂无数据" //如果没有数据会显示这个 lineChartView.drawGridBackgroundEnabled = true //绘制图形区域背景 lineChartView.gridBackgroundColor = ZHFColor.white //背景颜色 lineChartView.alpha = 0.5//背景透明度 //折线图描述文字和样式 lineChartView.chartDescription?.text = "波浪图描述" lineChartView.legend.textColor = ZHFColor.purple //描述文字颜色 lineChartView.legend.formSize = 10 //(图例大小)默认是8 lineChartView.legend.form = Legend.Form.circle//图例头部样式 //矩形:.square(默认值) 圆形:.circle 横线:.line 无:.none 空:.empty(与 .none 一样都不显示头部,但不同的是 empty 头部仍然会占一个位置) lineChartView.chartDescription?.position = CGPoint.init(x: lineChartView.frame.width - 30, y:lineChartView.frame.height - 20)//位置(及在lineChartView的中心点) lineChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小 lineChartView.chartDescription?.textColor = UIColor.red //设置交互样式 lineChartView.scaleYEnabled = false //取消Y轴缩放 lineChartView.doubleTapToZoomEnabled = true //双击缩放 lineChartView.dragEnabled = true //启用拖动手势 lineChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果 lineChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果摩擦系数(0~1)越小惯性越不明显 //修改背景色和边框样式 lineChartView.drawBordersEnabled = true //绘制图形区域边框 lineChartView.borderColor = ZHFColor.red //边框为红色 lineChartView.borderLineWidth = 2 //边框线条大小为2 } //设置x轴的样式属性 func setXAxisStyle(){ //轴线宽、颜色、刻度、间隔 lineChartView.xAxis.axisLineWidth = 2 //x轴宽度 lineChartView.xAxis.axisLineColor = .black //x轴颜色 lineChartView.xAxis.axisMinimum = -3 //最小刻度值 lineChartView.xAxis.axisMaximum = 15 //最大刻度值 lineChartView.xAxis.labelCount = 18//显示个数 lineChartView.xAxis.spaceMin = 1 //最小间隔 //文字属性 lineChartView.xAxis.labelPosition = .bottom //x轴上的数字显示在下方(默认显示在上方 .top .bottom .bothSided .topInside .bottomInside) lineChartView.xAxis.labelTextColor = .red //刻度文字颜色 lineChartView.xAxis.labelFont = .systemFont(ofSize: 13) //刻度文字大小 } //设置y轴的样式属性(分左、右侧) func setYAxisStyle(){ //右侧(默认显示) // lineChartView.rightAxis.drawLabelsEnabled = false //不绘制右侧Y轴文字 lineChartView.rightAxis.drawAxisLineEnabled = false //不显示右侧Y轴 lineChartView.rightAxis.enabled = false //禁用右侧的Y轴 //左侧 // lineChartView.leftAxis.inverted = true //刻度值反向排列(默认正向) // lineChartView.leftAxis.labelPosition = .insideChart //文字显示在内侧 //0刻度线 lineChartView.leftAxis.axisMinimum = 0 lineChartView.leftAxis.axisMaximum = 8 lineChartView.leftAxis.granularity = 1 //最小间隔 } @objc func updataData(){ let dic: [String: Any] = [ "5g" : [ "forty" : ["number" : [0,0,0,0,0,0, 0,0, 0, 0, 0,0], "channelsetting" : [38, 46, 54, 62, 102, 110, 118,126, 134,142,151,159]], "eighty" : ["number" : [5,5,0,0,0,1], "channelsetting" : [42,58,106,122,138,155]], "twenty" : ["number" : [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "channelsetting" : [36,40,44,48,52,56,60,64,100,104,108,112,116,120,124,128,32,136,140,144,149,153,157,161,165]] ], "2.4g" : [ "twenty" : ["number" : [5,0,0,0,0,4,0,0,0,0,2,0,0], "channelsetting" : [1,2,3,4,5,6,7,8,9,10,11,12,13]], "forty" : ["number" : [5,0,0,0,0,2,0,0,0,0,0,0], "channelsetting" : [2,3,4,5,6,7,8,9,10,11,12,13]], ] ] let dic24:[String:Any] = dic["2.4g"] as! [String : Any] let dic2420:[String:Any] = dic24["twenty"] as! [String : Any] let dic2420X:[NSInteger] = dic2420["channelsetting"] as! [NSInteger] let dic2420Y:[NSInteger] = dic2420["number"] as! [NSInteger] let dic2440:[String:Any] = dic24["forty"] as! [String : Any] let dic2440X:[NSInteger] = dic2440["channelsetting"] as! [NSInteger] let dic2440Y:[NSInteger] = dic2440["number"] as! [NSInteger] let chartData = LineChartData.init() //2.4g 20Hz 的图 if dic2420Y.count > 0 { add(xArr: dic2420X, yArr: dic2420Y, HzNumber: 24/5,chartData:chartData,fillColor: ZHFColor.zhf_strColor(hex: "000000")) } //2.4g 40Hz 的图 if dic2440Y.count > 0 { add(xArr: dic2440X, yArr: dic2440Y, HzNumber: 40/5,chartData:chartData,fillColor: ZHFColor.zhf_color(withHex: 0xff0000)) } } /* xArr: x轴数组 yArr: y轴数组 HzNumber: Hz/间隔代表值 chartData: 初始化的 chartData fillColor: 填充色 */ func add(xArr:[NSInteger],yArr:[NSInteger],HzNumber:Double,chartData:LineChartData,fillColor:UIColor){ var labelStr1:String = "" var labelColor1: UIColor = ZHFColor.orange var isHaveLabel:Bool = true for i in 0 ..< xArr.count { let x = xArr[i] let y = yArr[i] if y != 0{ if isHaveLabel == true{ labelStr1 = "波浪线" labelColor1 = ZHFColor.orange } else{ labelStr1 = "" labelColor1 = ZHFColor.clear } var dataEntries1 = [ChartDataEntry]() let entry1 = ChartDataEntry.init(x: Double(x) - HzNumber, y: -Double(y) - 1) let entry2 = ChartDataEntry.init(x: Double(x), y: Double(y)) let entry3 = ChartDataEntry.init(x: Double(x) + HzNumber, y: -Double(y) - 1) dataEntries1.append(entry1) dataEntries1.append(entry2) dataEntries1.append(entry3) let chartDataSet1 = LineChartDataSet(values: dataEntries1, label: labelStr1) chartDataSet1.mode = .horizontalBezier //(.line、.cubicBezier、.horizontalBezier、.stepped) chartDataSet1.setColor(labelColor1) chartDataSet1.lineWidth = 0 chartDataSet1.drawCirclesEnabled = false //不绘制拐点 chartDataSet1.fillAlpha = 0.3 chartDataSet1.fillColor = fillColor chartDataSet1.drawFilledEnabled = true //绘制上填充色 chartDataSet1.fillFormatter = DefaultFillFormatter { _,_ -> CGFloat in return CGFloat(self.lineChartView.leftAxis.axisMinimum) //向下绘制填充区域 } chartData.addDataSet(chartDataSet1) isHaveLabel = false } } lineChartView.data = chartData } } extension WaveformChartVC: ChartViewDelegate{ //1.点击选中 func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) { ZHFLog(message: "点击选中") } //2.没有选中 func chartValueNothingSelected(_ chartView: ChartViewBase) { ZHFLog(message: "没有选中") } //3.捏合放大或缩小 func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) { ZHFLog(message: "捏合放大或缩小") } //4.拖拽图表 func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) { ZHFLog(message: "拖拽图表") } } ================================================ FILE: ChartsUnderstandAndUsage/ZHFColor.swift ================================================ // // ZHFColor.swift // AmazedBox // // Created by lantian on 2018/5/9. // Copyright © 2018年 张海峰. All rights reserved. // //Charts框架地址 //https://github.com/danielgindi/Charts.git //该demo地址 //https://github.com/FighterLightning/ChartsUnderstandAndUsage.git import UIKit class ZHFColor: UIColor { /// 主题色(及选中颜色) open class var zhf_selectColor: UIColor { //橙色 get { return self.zhf_color(withHex: 0xF98507) } } /// 标题字体颜色 open class var zhf33_titleTextColor: UIColor { get { return self.zhf_color(withHex: 0x333333) } } /// 内容字体颜色1 open class var zhf88_contentTextColor: UIColor { get { return self.zhf_color(withHex: 0x888888) } } /// 内容字体颜色2 open class var zhf66_contentTextColor: UIColor { get { return self.zhf_color(withHex: 0x666666) } } /// 背景色1 open class var zhfe8_backGroundColor: UIColor { get { return self.zhf_color(withHex: 0xe8e8e8) } } /// 背景色2 open class var zhff9_backGroundColor: UIColor { get { return self.zhf_color(withHex: 0xf9f9f9) } } /// 分割线的颜色1 open class var zhfcc_lineColor: UIColor { get { return self.zhf_color(withHex: 0xcccccc) } } /// 分割线的颜色2 open class var zhf_lineColor: UIColor { get { return self.zhf_color(withHex: 0xebf0f5) } } /// 随机的颜色 class func zhf_randomColor() -> UIColor { let r = CGFloat(arc4random() % 256) / 255.0 let g = CGFloat(arc4random() % 256) / 255.0 let b = CGFloat(arc4random() % 256) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: 1.0) } /// 十六进制颜色 0xFFFFFF (0x六位颜色) class func zhf_color(withHex: UInt32) -> UIColor { let r = ((CGFloat)((withHex & 0xFF0000) >> 16)) / 255.0 let g = ((CGFloat)((withHex & 0xFF00) >> 8)) / 255.0 let b = ((CGFloat)(withHex & 0xFF)) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: 1.0) } /// 十六进制颜色(带透明度) 0xFFFFFF (0x六位颜色) class func zhf_colorAlpha(withHex: UInt32,alpha: CGFloat) -> UIColor { let r = ((CGFloat)((withHex & 0xFF0000) >> 16)) / 255.0 let g = ((CGFloat)((withHex & 0xFF00) >> 8)) / 255.0 let b = ((CGFloat)(withHex & 0xFF)) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: alpha) } /// 0~255 颜色 /// red(0~255) /// green(0~255) /// blue(0~255) class func zhf_color(withRed: UInt8, green: UInt8, blue: UInt8) -> UIColor { let r = CGFloat(withRed) / 255.0 let g = CGFloat(green) / 255.0 let b = CGFloat(blue) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: 1.0) } /// 字符串颜色 "六位颜色" class func zhf_strColor(hex: String) -> UIColor { let scanner = Scanner(string: hex) scanner.scanLocation = 0 var rgbValue: UInt64 = 0 scanner.scanHexInt64(&rgbValue) let r = ((CGFloat)((rgbValue & 0xFF0000) >> 16)) / 255.0 let g = ((CGFloat)((rgbValue & 0xFF00) >> 8)) / 255.0 let b = ((CGFloat)(rgbValue & 0xFF)) / 255.0 return UIColor(red: r, green: g, blue: b, alpha: 1.0) } } ================================================ FILE: ChartsUnderstandAndUsage.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 094F73FE91B1045A7744156A /* Pods_ChartsUnderstandAndUsage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2986C8E69D024580F6229424 /* Pods_ChartsUnderstandAndUsage.framework */; }; E330184E2151FE3200151C8B /* PieChartHalfVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E330184D2151FE3200151C8B /* PieChartHalfVC.swift */; }; E346EC75219EC6A200861746 /* WaveformChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E346EC74219EC6A200861746 /* WaveformChartVC.swift */; }; E35A39B8214F668F002B545A /* LineChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39B7214F668F002B545A /* LineChartVC.swift */; }; E35A39BA214F66E9002B545A /* CombinedChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39B9214F66E9002B545A /* CombinedChartVC.swift */; }; E35A39BC214F6711002B545A /* ScatterChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39BB214F6711002B545A /* ScatterChartVC.swift */; }; E35A39BE214F6748002B545A /* BubbleChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39BD214F6748002B545A /* BubbleChartVC.swift */; }; E35A39C0214F676B002B545A /* CandleStickChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39BF214F676B002B545A /* CandleStickChartVC.swift */; }; E35A39C62150BDE0002B545A /* LineFilledChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E35A39C52150BDE0002B545A /* LineFilledChartVC.swift */; }; E3800A812178579E007C9744 /* BaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3800A802178579E007C9744 /* BaseVC.swift */; }; E3800A8621785EDC007C9744 /* PieChartPolylineVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3800A8521785EDC007C9744 /* PieChartPolylineVC.swift */; }; E3800A8A217867C9007C9744 /* BarChartWaveVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3800A89217867C9007C9744 /* BarChartWaveVC.swift */; }; E38958422149054700B960CD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38958412149054700B960CD /* AppDelegate.swift */; }; E38958442149054700B960CD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38958432149054700B960CD /* ViewController.swift */; }; E38958472149054700B960CD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E38958452149054700B960CD /* Main.storyboard */; }; E38958492149054800B960CD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E38958482149054800B960CD /* Assets.xcassets */; }; E389584C2149054800B960CD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E389584A2149054800B960CD /* LaunchScreen.storyboard */; }; E38958572149054800B960CD /* ChartsUnderstandAndUsageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38958562149054800B960CD /* ChartsUnderstandAndUsageTests.swift */; }; E38958622149054800B960CD /* ChartsUnderstandAndUsageUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38958612149054800B960CD /* ChartsUnderstandAndUsageUITests.swift */; }; E3895870214909D300B960CD /* ZHFColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E389586F214909D300B960CD /* ZHFColor.swift */; }; E3895872214910AC00B960CD /* BarChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3895871214910AC00B960CD /* BarChartVC.swift */; }; E3895878214A521B00B960CD /* PieChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3895877214A521B00B960CD /* PieChartVC.swift */; }; E389587A214A650A00B960CD /* RadarChartVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3895879214A650A00B960CD /* RadarChartVC.swift */; }; E3C36A5F2150FF1B00439354 /* BalloonMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3C36A5E2150FF1B00439354 /* BalloonMarker.swift */; }; FA3EAA12283639D600964992 /* Charts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA3EAA11283639D600964992 /* Charts.framework */; }; FA3EAA1528363E5E00964992 /* Pods_ChartsUnderstandAndUsage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2986C8E69D024580F6229424 /* Pods_ChartsUnderstandAndUsage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ E38958532149054800B960CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E38958362149054700B960CD /* Project object */; proxyType = 1; remoteGlobalIDString = E389583D2149054700B960CD; remoteInfo = ChartsUnderstandAndUsage; }; E389585E2149054800B960CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E38958362149054700B960CD /* Project object */; proxyType = 1; remoteGlobalIDString = E389583D2149054700B960CD; remoteInfo = ChartsUnderstandAndUsage; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ FA3EAA14283639D600964992 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( FA3EAA1528363E5E00964992 /* Pods_ChartsUnderstandAndUsage.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 2986C8E69D024580F6229424 /* Pods_ChartsUnderstandAndUsage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ChartsUnderstandAndUsage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 80808BA9743F4CF4082C5819 /* Pods-ChartsUnderstandAndUsage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChartsUnderstandAndUsage.release.xcconfig"; path = "Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.release.xcconfig"; sourceTree = ""; }; 8A8BDE7C0508A2EFCEA84120 /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChartsUnderstandAndUsage.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.debug.xcconfig"; sourceTree = ""; }; E330184D2151FE3200151C8B /* PieChartHalfVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieChartHalfVC.swift; sourceTree = ""; }; E346EC74219EC6A200861746 /* WaveformChartVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WaveformChartVC.swift; sourceTree = ""; }; E35A39B7214F668F002B545A /* LineChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartVC.swift; sourceTree = ""; }; E35A39B9214F66E9002B545A /* CombinedChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinedChartVC.swift; sourceTree = ""; }; E35A39BB214F6711002B545A /* ScatterChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScatterChartVC.swift; sourceTree = ""; }; E35A39BD214F6748002B545A /* BubbleChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BubbleChartVC.swift; sourceTree = ""; }; E35A39BF214F676B002B545A /* CandleStickChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CandleStickChartVC.swift; sourceTree = ""; }; E35A39C52150BDE0002B545A /* LineFilledChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineFilledChartVC.swift; sourceTree = ""; }; E3800A802178579E007C9744 /* BaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseVC.swift; sourceTree = ""; }; E3800A8521785EDC007C9744 /* PieChartPolylineVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieChartPolylineVC.swift; sourceTree = ""; }; E3800A89217867C9007C9744 /* BarChartWaveVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartWaveVC.swift; sourceTree = ""; }; E389583E2149054700B960CD /* ChartsUnderstandAndUsage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChartsUnderstandAndUsage.app; sourceTree = BUILT_PRODUCTS_DIR; }; E38958412149054700B960CD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E38958432149054700B960CD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; E38958462149054700B960CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; E38958482149054800B960CD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; E389584B2149054800B960CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; E389584D2149054800B960CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E38958522149054800B960CD /* ChartsUnderstandAndUsageTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChartsUnderstandAndUsageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E38958562149054800B960CD /* ChartsUnderstandAndUsageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartsUnderstandAndUsageTests.swift; sourceTree = ""; }; E38958582149054800B960CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E389585D2149054800B960CD /* ChartsUnderstandAndUsageUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChartsUnderstandAndUsageUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E38958612149054800B960CD /* ChartsUnderstandAndUsageUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartsUnderstandAndUsageUITests.swift; sourceTree = ""; }; E38958632149054800B960CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E389586F214909D300B960CD /* ZHFColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZHFColor.swift; sourceTree = ""; }; E3895871214910AC00B960CD /* BarChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarChartVC.swift; sourceTree = ""; }; E389587321491CFF00B960CD /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E3895877214A521B00B960CD /* PieChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PieChartVC.swift; sourceTree = ""; }; E3895879214A650A00B960CD /* RadarChartVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarChartVC.swift; sourceTree = ""; }; E3C36A5E2150FF1B00439354 /* BalloonMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalloonMarker.swift; sourceTree = ""; }; FA3EAA0D283639BE00964992 /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FA3EAA11283639D600964992 /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ E389583B2149054700B960CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 094F73FE91B1045A7744156A /* Pods_ChartsUnderstandAndUsage.framework in Frameworks */, FA3EAA12283639D600964992 /* Charts.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E389584F2149054800B960CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; E389585A2149054800B960CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C5ADDC9AD4F5D81F60A4FA36 /* Frameworks */ = { isa = PBXGroup; children = ( FA3EAA11283639D600964992 /* Charts.framework */, FA3EAA0D283639BE00964992 /* Charts.framework */, E389587321491CFF00B960CD /* Charts.framework */, 2986C8E69D024580F6229424 /* Pods_ChartsUnderstandAndUsage.framework */, ); name = Frameworks; sourceTree = ""; }; E38958352149054700B960CD = { isa = PBXGroup; children = ( E38958402149054700B960CD /* ChartsUnderstandAndUsage */, E38958552149054800B960CD /* ChartsUnderstandAndUsageTests */, E38958602149054800B960CD /* ChartsUnderstandAndUsageUITests */, E389583F2149054700B960CD /* Products */, E87F0A782314E4C08224B6F5 /* Pods */, C5ADDC9AD4F5D81F60A4FA36 /* Frameworks */, ); sourceTree = ""; }; E389583F2149054700B960CD /* Products */ = { isa = PBXGroup; children = ( E389583E2149054700B960CD /* ChartsUnderstandAndUsage.app */, E38958522149054800B960CD /* ChartsUnderstandAndUsageTests.xctest */, E389585D2149054800B960CD /* ChartsUnderstandAndUsageUITests.xctest */, ); name = Products; sourceTree = ""; }; E38958402149054700B960CD /* ChartsUnderstandAndUsage */ = { isa = PBXGroup; children = ( E389586F214909D300B960CD /* ZHFColor.swift */, E3C36A5E2150FF1B00439354 /* BalloonMarker.swift */, E38958412149054700B960CD /* AppDelegate.swift */, E38958432149054700B960CD /* ViewController.swift */, E3800A802178579E007C9744 /* BaseVC.swift */, E3895871214910AC00B960CD /* BarChartVC.swift */, E3800A89217867C9007C9744 /* BarChartWaveVC.swift */, E3895877214A521B00B960CD /* PieChartVC.swift */, E330184D2151FE3200151C8B /* PieChartHalfVC.swift */, E3800A8521785EDC007C9744 /* PieChartPolylineVC.swift */, E3895879214A650A00B960CD /* RadarChartVC.swift */, E35A39B7214F668F002B545A /* LineChartVC.swift */, E35A39C52150BDE0002B545A /* LineFilledChartVC.swift */, E35A39B9214F66E9002B545A /* CombinedChartVC.swift */, E35A39BB214F6711002B545A /* ScatterChartVC.swift */, E35A39BF214F676B002B545A /* CandleStickChartVC.swift */, E35A39BD214F6748002B545A /* BubbleChartVC.swift */, E346EC74219EC6A200861746 /* WaveformChartVC.swift */, E38958452149054700B960CD /* Main.storyboard */, E38958482149054800B960CD /* Assets.xcassets */, E389584A2149054800B960CD /* LaunchScreen.storyboard */, E389584D2149054800B960CD /* Info.plist */, ); path = ChartsUnderstandAndUsage; sourceTree = ""; }; E38958552149054800B960CD /* ChartsUnderstandAndUsageTests */ = { isa = PBXGroup; children = ( E38958562149054800B960CD /* ChartsUnderstandAndUsageTests.swift */, E38958582149054800B960CD /* Info.plist */, ); path = ChartsUnderstandAndUsageTests; sourceTree = ""; }; E38958602149054800B960CD /* ChartsUnderstandAndUsageUITests */ = { isa = PBXGroup; children = ( E38958612149054800B960CD /* ChartsUnderstandAndUsageUITests.swift */, E38958632149054800B960CD /* Info.plist */, ); path = ChartsUnderstandAndUsageUITests; sourceTree = ""; }; E87F0A782314E4C08224B6F5 /* Pods */ = { isa = PBXGroup; children = ( 8A8BDE7C0508A2EFCEA84120 /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */, 80808BA9743F4CF4082C5819 /* Pods-ChartsUnderstandAndUsage.release.xcconfig */, ); name = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ E389583D2149054700B960CD /* ChartsUnderstandAndUsage */ = { isa = PBXNativeTarget; buildConfigurationList = E38958662149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsage" */; buildPhases = ( 379ADA5B89F08EA9DC8140D4 /* [CP] Check Pods Manifest.lock */, E389583A2149054700B960CD /* Sources */, E389583B2149054700B960CD /* Frameworks */, E389583C2149054700B960CD /* Resources */, 1310E3972A00FA57A6DA7B17 /* [CP] Embed Pods Frameworks */, FA3EAA14283639D600964992 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ChartsUnderstandAndUsage; productName = ChartsUnderstandAndUsage; productReference = E389583E2149054700B960CD /* ChartsUnderstandAndUsage.app */; productType = "com.apple.product-type.application"; }; E38958512149054800B960CD /* ChartsUnderstandAndUsageTests */ = { isa = PBXNativeTarget; buildConfigurationList = E38958692149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsageTests" */; buildPhases = ( E389584E2149054800B960CD /* Sources */, E389584F2149054800B960CD /* Frameworks */, E38958502149054800B960CD /* Resources */, ); buildRules = ( ); dependencies = ( E38958542149054800B960CD /* PBXTargetDependency */, ); name = ChartsUnderstandAndUsageTests; productName = ChartsUnderstandAndUsageTests; productReference = E38958522149054800B960CD /* ChartsUnderstandAndUsageTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; E389585C2149054800B960CD /* ChartsUnderstandAndUsageUITests */ = { isa = PBXNativeTarget; buildConfigurationList = E389586C2149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsageUITests" */; buildPhases = ( E38958592149054800B960CD /* Sources */, E389585A2149054800B960CD /* Frameworks */, E389585B2149054800B960CD /* Resources */, ); buildRules = ( ); dependencies = ( E389585F2149054800B960CD /* PBXTargetDependency */, ); name = ChartsUnderstandAndUsageUITests; productName = ChartsUnderstandAndUsageUITests; productReference = E389585D2149054800B960CD /* ChartsUnderstandAndUsageUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ E38958362149054700B960CD /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0940; LastUpgradeCheck = 1320; ORGANIZATIONNAME = "张海峰"; TargetAttributes = { E389583D2149054700B960CD = { CreatedOnToolsVersion = 9.4.1; }; E38958512149054800B960CD = { CreatedOnToolsVersion = 9.4.1; TestTargetID = E389583D2149054700B960CD; }; E389585C2149054800B960CD = { CreatedOnToolsVersion = 9.4.1; TestTargetID = E389583D2149054700B960CD; }; }; }; buildConfigurationList = E38958392149054700B960CD /* Build configuration list for PBXProject "ChartsUnderstandAndUsage" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = E38958352149054700B960CD; productRefGroup = E389583F2149054700B960CD /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( E389583D2149054700B960CD /* ChartsUnderstandAndUsage */, E38958512149054800B960CD /* ChartsUnderstandAndUsageTests */, E389585C2149054800B960CD /* ChartsUnderstandAndUsageUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ E389583C2149054700B960CD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( E389584C2149054800B960CD /* LaunchScreen.storyboard in Resources */, E38958492149054800B960CD /* Assets.xcassets in Resources */, E38958472149054700B960CD /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; E38958502149054800B960CD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; E389585B2149054800B960CD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 1310E3972A00FA57A6DA7B17 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 379ADA5B89F08EA9DC8140D4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-ChartsUnderstandAndUsage-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ E389583A2149054700B960CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E389587A214A650A00B960CD /* RadarChartVC.swift in Sources */, E35A39C62150BDE0002B545A /* LineFilledChartVC.swift in Sources */, E3800A8A217867C9007C9744 /* BarChartWaveVC.swift in Sources */, E35A39BC214F6711002B545A /* ScatterChartVC.swift in Sources */, E35A39C0214F676B002B545A /* CandleStickChartVC.swift in Sources */, E3800A812178579E007C9744 /* BaseVC.swift in Sources */, E3C36A5F2150FF1B00439354 /* BalloonMarker.swift in Sources */, E330184E2151FE3200151C8B /* PieChartHalfVC.swift in Sources */, E346EC75219EC6A200861746 /* WaveformChartVC.swift in Sources */, E35A39B8214F668F002B545A /* LineChartVC.swift in Sources */, E3800A8621785EDC007C9744 /* PieChartPolylineVC.swift in Sources */, E3895870214909D300B960CD /* ZHFColor.swift in Sources */, E38958442149054700B960CD /* ViewController.swift in Sources */, E35A39BA214F66E9002B545A /* CombinedChartVC.swift in Sources */, E38958422149054700B960CD /* AppDelegate.swift in Sources */, E35A39BE214F6748002B545A /* BubbleChartVC.swift in Sources */, E3895878214A521B00B960CD /* PieChartVC.swift in Sources */, E3895872214910AC00B960CD /* BarChartVC.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E389584E2149054800B960CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E38958572149054800B960CD /* ChartsUnderstandAndUsageTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E38958592149054800B960CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E38958622149054800B960CD /* ChartsUnderstandAndUsageUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ E38958542149054800B960CD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E389583D2149054700B960CD /* ChartsUnderstandAndUsage */; targetProxy = E38958532149054800B960CD /* PBXContainerItemProxy */; }; E389585F2149054800B960CD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E389583D2149054700B960CD /* ChartsUnderstandAndUsage */; targetProxy = E389585E2149054800B960CD /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ E38958452149054700B960CD /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( E38958462149054700B960CD /* Base */, ); name = Main.storyboard; sourceTree = ""; }; E389584A2149054800B960CD /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( E389584B2149054800B960CD /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ E38958642149054800B960CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; E38958652149054800B960CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; E38958672149054800B960CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8A8BDE7C0508A2EFCEA84120 /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 8CB6874GM5; INFOPLIST_FILE = ChartsUnderstandAndUsage/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; E38958682149054800B960CD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 80808BA9743F4CF4082C5819 /* Pods-ChartsUnderstandAndUsage.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 8CB6874GM5; INFOPLIST_FILE = ChartsUnderstandAndUsage/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; E389586A2149054800B960CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = KF97C6R67W; INFOPLIST_FILE = ChartsUnderstandAndUsageTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChartsUnderstandAndUsage.app/ChartsUnderstandAndUsage"; }; name = Debug; }; E389586B2149054800B960CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = KF97C6R67W; INFOPLIST_FILE = ChartsUnderstandAndUsageTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ChartsUnderstandAndUsage.app/ChartsUnderstandAndUsage"; }; name = Release; }; E389586D2149054800B960CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = KF97C6R67W; INFOPLIST_FILE = ChartsUnderstandAndUsageUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ChartsUnderstandAndUsage; }; name = Debug; }; E389586E2149054800B960CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = KF97C6R67W; INFOPLIST_FILE = ChartsUnderstandAndUsageUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = ChartsUnderstandAndUsage.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ChartsUnderstandAndUsage; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ E38958392149054700B960CD /* Build configuration list for PBXProject "ChartsUnderstandAndUsage" */ = { isa = XCConfigurationList; buildConfigurations = ( E38958642149054800B960CD /* Debug */, E38958652149054800B960CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E38958662149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsage" */ = { isa = XCConfigurationList; buildConfigurations = ( E38958672149054800B960CD /* Debug */, E38958682149054800B960CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E38958692149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsageTests" */ = { isa = XCConfigurationList; buildConfigurations = ( E389586A2149054800B960CD /* Debug */, E389586B2149054800B960CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E389586C2149054800B960CD /* Build configuration list for PBXNativeTarget "ChartsUnderstandAndUsageUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( E389586D2149054800B960CD /* Debug */, E389586E2149054800B960CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = E38958362149054700B960CD /* Project object */; } ================================================ FILE: ChartsUnderstandAndUsage.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ChartsUnderstandAndUsage.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ChartsUnderstandAndUsage.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState ChartsUnderstandAndUsage.xcscheme_^#shared#^_ orderHint 2 ================================================ FILE: ChartsUnderstandAndUsage.xcodeproj/xcuserdata/zhanghaifeng.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState ChartsUnderstandAndUsage.xcscheme orderHint 2 ChartsUnderstandAndUsage.xcscheme_^#shared#^_ orderHint 2 ================================================ FILE: ChartsUnderstandAndUsage.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ChartsUnderstandAndUsage.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ChartsUnderstandAndUsage.xcworkspace/xcuserdata/zhanghaifeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: ChartsUnderstandAndUsageTests/ChartsUnderstandAndUsageTests.swift ================================================ // // ChartsUnderstandAndUsageTests.swift // ChartsUnderstandAndUsageTests // // Created by 张海峰 on 2018/9/12. // Copyright © 2018年 张海峰. All rights reserved. // import XCTest @testable import ChartsUnderstandAndUsage class ChartsUnderstandAndUsageTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: ChartsUnderstandAndUsageTests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: ChartsUnderstandAndUsageUITests/ChartsUnderstandAndUsageUITests.swift ================================================ // // ChartsUnderstandAndUsageUITests.swift // ChartsUnderstandAndUsageUITests // // Created by 张海峰 on 2018/9/12. // Copyright © 2018年 张海峰. All rights reserved. // import XCTest class ChartsUnderstandAndUsageUITests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. XCUIApplication().launch() // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // Use recording to get started writing UI tests. // Use XCTAssert and related functions to verify your tests produce the correct results. } } ================================================ FILE: ChartsUnderstandAndUsageUITests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: Podfile ================================================ #Uncomment the to define a global platform for your project platform :ios, '9.0' target 'ChartsUnderstandAndUsage' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! pod 'Charts' end ================================================ FILE: Pods/Charts/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 Daniel Cohen Gindi & Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Pods/Charts/README.md ================================================ **Version 3.1.1**, synced to [MPAndroidChart #f6a398b](https://github.com/PhilJay/MPAndroidChart/commit/f6a398b) ![alt tag](https://raw.github.com/danielgindi/Charts/master/Assets/feature_graphic.png) ![Supported Platforms](https://img.shields.io/cocoapods/p/Charts.svg) [![Releases](https://img.shields.io/github/release/danielgindi/Charts.svg)](https://github.com/danielgindi/Charts/releases) [![Latest pod release](https://img.shields.io/cocoapods/v/Charts.svg)](http://cocoapods.org/pods/charts) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Build Status](https://travis-ci.org/danielgindi/Charts.svg?branch=master)](https://travis-ci.org/danielgindi/Charts) [![codecov](https://codecov.io/gh/danielgindi/Charts/branch/master/graph/badge.svg)](https://codecov.io/gh/danielgindi/Charts) [![Join the chat at https://gitter.im/danielgindi/Charts](https://badges.gitter.im/danielgindi/Charts.svg)](https://gitter.im/danielgindi/Charts?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ### Just a heads up: Charts 3.0 has some breaking changes. Please read [the release/migration notes](https://github.com/danielgindi/Charts/releases/tag/v3.0.0). ### Another heads up: ChartsRealm is now in a [separate repo](https://github.com/danielgindi/ChartsRealm). Pods is also now `Charts` and `ChartsRealm`, instead of ~`Charts/Core`~ and ~`Charts/Realm`~ * Xcode 9.3 / Swift 4.1 * iOS >= 8.0 (Use as an **Embedded** Framework) * tvOS >= 9.0 * macOS >= 10.11 Okay so there's this beautiful library called [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) by [Philipp Jahoda](https://www.linkedin.com/in/philippjahoda) which has become very popular amongst Android developers, and in the meanwhile there's no decent charting solution for iOS. I've chosen to write it in `Swift` as it can be highly optimized by the compiler, and can be used in both `Swift` and `ObjC` project. The demo project is written in `ObjC` to demonstrate how it works. **An amazing feature** of this library now, for Android, iOS, tvOS and macOS, is the time it saves you when developing for both platforms, as the learning curve is singleton- it happens only once, and the code stays very similar so developers don't have to go around and re-invent the app to produce the same output with a different library. (And that's not even considering the fact that there's not really another good choice out there currently...) ## Having trouble running the demo? * `ChartsDemo/ChartsDemo.xcodeproj` is the demo project for iOS/tvOS * `ChartsDemo-OSX/ChartsDemo-OSX.xcodeproj` is the demo project for macOS * Make sure you are running a supported version of Xcode. * Usually it is specified here a few lines above. * In most cases it will be the latest Xcode version. * Make sure that your project supports Swift 3.0 * Optional: Run `carthage checkout` in the project folder, to fetch dependencies (i.e testing dependencies). * If you don't have Carthage - you can get it [here](https://github.com/Carthage/Carthage/releases). ## Usage In order to correctly compile: 1. Drag the `Charts.xcodeproj` to your project 2. Go to your target's settings, hit the "+" under the "Embedded Binaries" section, and select the Charts.framework 3. `@import Charts` 4. When using Swift in an ObjC project: - You need to import your Bridging Header. Usually it is "*YourProject-Swift.h*", so in ChartsDemo it's "*ChartsDemo-Swift.h*". Do not try to actually include "*ChartsDemo-Swift.h*" in your project :-) - (Xcode 8.1 and earlier) Under "Build Options", mark "Embedded Content Contains Swift Code" - (Xcode 8.2+) Under "Build Options", mark "Always Embed Swift Standard Libraries" 5. When using [Realm.io](https://realm.io/): - Note that the Realm framework is not linked with Charts - it is only there for *optional* bindings. Which means that you need to have the framework in your project, and in a compatible version to whatever is compiled with Charts. We will do our best to always compile against the latest version. - You'll need to add `ChartsRealm` as a dependency too. ## 3rd party tutorials * [Using Realm and Charts with Swift 3 in iOS 10 (Sami Korpela)](https://medium.com/@skoli/using-realm-and-charts-with-swift-3-in-ios-10-40c42e3838c0#.2gyymwfh8) * [Creating a Line Chart in Swift 3 and iOS 10 (Osian Smith)](https://medium.com/@OsianSmith/creating-a-line-chart-in-swift-3-and-ios-10-2f647c95392e) * [Beginning Set-up and Example Using Charts with Swift 3](https://github.com/annalizhaz/ChartsForSwiftBasic) * Want your tutorial to show here? Create a PR! ## Troubleshooting #### Can't compile? * Please note the difference between installing a compiled framework from CocoaPods or Carthage, and copying the source code. * Please read the **Usage** section again. * Search in the issues * Try to politely ask in the issues section #### Other problems / feature requests * Search in the issues * Try to politely ask in the issues section ## CocoaPods Install Add `pod 'Charts'` to your Podfile. "Charts" is the name of the library. For [Realm](https://realm.io/) support, please add `pod 'ChartsRealm'` too. **Note:** ~~`pod 'ios-charts'`~~ is not the correct library, and refers to a different project by someone else. ## Carthage Install Charts now include Carthage prebuilt binaries. ```carthage github "danielgindi/Charts" == 3.1.1 github "danielgindi/Charts" ~> 3.1.1 ``` In order to build the binaries for a new release, use `carthage build --no-skip-current && carthage archive Charts`. ## 3rd party bindings Xamarin (by @Flash3001): *iOS* - [GitHub](https://github.com/Flash3001/iOSCharts.Xamarin)/[NuGet](https://www.nuget.org/packages/iOSCharts/). *Android* - [GitHub](https://github.com/Flash3001/MPAndroidChart.Xamarin)/[NuGet](https://www.nuget.org/packages/MPAndroidChart/). ## Help If you like what you see here, and want to support the work being done in this repository, you could: * Contribute code, issues and pull requests * Let people know this library exists (:fire: spread the word :fire:) * [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=68UL6Y8KUPS96) (You can buy me a beer, or you can buy me dinner :-) **Note:** The author of [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) is the reason that this library exists, and is accepting [donations](https://github.com/PhilJay/MPAndroidChart#donations) on his page. He deserves them! Questions & Issues ----- If you are having questions or problems, you should: - Make sure you are using the latest version of the library. Check the [**release-section**](https://github.com/danielgindi/Charts/releases). - Study the Android version's [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) - Study the (Still incomplete [![Doc-Percent](https://img.shields.io/cocoapods/metrics/doc-percent/Charts.svg)](http://cocoadocs.org/docsets/Charts/)) [**Pod-Documentation**](http://cocoadocs.org/docsets/Charts/) - Search or open questions on [**stackoverflow**](http://stackoverflow.com/questions/tagged/ios-charts) with the `ios-charts` tag - Search [**known issues**](https://github.com/danielgindi/Charts/issues) for your problem (open and closed) - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) Features ======= **Core features:** - 8 different chart types - Scaling on both axes (with touch-gesture, axes separately or pinch-zoom) - Dragging / Panning (with touch-gesture) - Combined-Charts (line-, bar-, scatter-, candle-stick-, bubble-) - Dual (separate) Axes - Customizable Axes (both x- and y-axis) - Highlighting values (with customizable popup-views) - Save chart to camera-roll / export to PNG/JPEG - Predefined color templates - Legends (generated automatically, customizable) - Animations (build up animations, on both x- and y-axis) - Limit lines (providing additional information, maximums, ...) - Fully customizable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) - Plotting data directly from [**Realm.io**](https://realm.io) mobile database ([here](https://github.com/danielgindi/ChartsRealm)) **Chart types:** *Screenshots are currently taken from the original repository, as they render exactly the same :-)* - **LineChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) - **LineChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) - **LineChart (cubic lines)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) - **LineChart (gradient fill)** ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) - **Combined-Chart (bar- and linechart in this case)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) - **BarChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) - **BarChart (grouped DataSets)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) - **Horizontal-BarChart** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) - **PieChart (with selection, ...)** ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) - **ScatterChart** (with squares, triangles, circles, ... and more) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) - **CandleStickChart** (for financial data) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) - **BubbleChart** (area covered by bubbles indicates the value) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) - **RadarChart** (spider web chart) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) Documentation ======= Currently there's no need for documentation for the iOS/tvOS/macOS version, as the API is **95% the same** as on Android. You can read the official [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) documentation here: [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) Or you can see the [**ChartsDemo**](https://github.com/danielgindi/Charts/tree/master/ChartsDemo) project and learn the how-tos from it. Special Thanks ======= Goes to [@liuxuan30](https://github.com/liuxuan30), [@petester42](https://github.com/petester42) and [@AlBirdie](https://github.com/AlBirdie) for new features, bugfixes, and lots and lots of involvement in our open-sourced community! You guys are a huge help to all of those coming here with questions and issues, and I couldn't respond to all of those without you. License ======= Copyright 2016 Daniel Cohen Gindi & Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Pods/Charts/Source/Charts/Animation/Animator.swift ================================================ // // Animator.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartAnimatorDelegate) public protocol AnimatorDelegate { /// Called when the Animator has stepped. func animatorUpdated(_ animator: Animator) /// Called when the Animator has stopped. func animatorStopped(_ animator: Animator) } @objc(ChartAnimator) open class Animator: NSObject { @objc open weak var delegate: AnimatorDelegate? @objc open var updateBlock: (() -> Void)? @objc open var stopBlock: (() -> Void)? /// the phase that is animated and influences the drawn values on the x-axis @objc open var phaseX: Double = 1.0 /// the phase that is animated and influences the drawn values on the y-axis @objc open var phaseY: Double = 1.0 private var _startTimeX: TimeInterval = 0.0 private var _startTimeY: TimeInterval = 0.0 private var _displayLink: NSUIDisplayLink? private var _durationX: TimeInterval = 0.0 private var _durationY: TimeInterval = 0.0 private var _endTimeX: TimeInterval = 0.0 private var _endTimeY: TimeInterval = 0.0 private var _endTime: TimeInterval = 0.0 private var _enabledX: Bool = false private var _enabledY: Bool = false private var _easingX: ChartEasingFunctionBlock? private var _easingY: ChartEasingFunctionBlock? public override init() { super.init() } deinit { stop() } @objc open func stop() { guard _displayLink != nil else { return } _displayLink?.remove(from: .main, forMode: .common) _displayLink = nil _enabledX = false _enabledY = false // If we stopped an animation in the middle, we do not want to leave it like this if phaseX != 1.0 || phaseY != 1.0 { phaseX = 1.0 phaseY = 1.0 delegate?.animatorUpdated(self) updateBlock?() } delegate?.animatorStopped(self) stopBlock?() } private func updateAnimationPhases(_ currentTime: TimeInterval) { if _enabledX { let elapsedTime: TimeInterval = currentTime - _startTimeX let duration: TimeInterval = _durationX var elapsed: TimeInterval = elapsedTime if elapsed > duration { elapsed = duration } phaseX = _easingX?(elapsed, duration) ?? elapsed / duration } if _enabledY { let elapsedTime: TimeInterval = currentTime - _startTimeY let duration: TimeInterval = _durationY var elapsed: TimeInterval = elapsedTime if elapsed > duration { elapsed = duration } phaseY = _easingY?(elapsed, duration) ?? elapsed / duration } } @objc private func animationLoop() { let currentTime: TimeInterval = CACurrentMediaTime() updateAnimationPhases(currentTime) delegate?.animatorUpdated(self) updateBlock?() if currentTime >= _endTime { stop() } } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingX: an easing function for the animation on the x axis /// - parameter easingY: an easing function for the animation on the y axis @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingX: ChartEasingFunctionBlock?, easingY: ChartEasingFunctionBlock?) { stop() _startTimeX = CACurrentMediaTime() _startTimeY = _startTimeX _durationX = xAxisDuration _durationY = yAxisDuration _endTimeX = _startTimeX + xAxisDuration _endTimeY = _startTimeY + yAxisDuration _endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY _enabledX = xAxisDuration > 0.0 _enabledY = yAxisDuration > 0.0 _easingX = easingX _easingY = easingY // Take care of the first frame if rendering is already scheduled... updateAnimationPhases(_startTimeX) if _enabledX || _enabledY { _displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop)) _displayLink?.add(to: RunLoop.main, forMode: .common) } } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOptionX: the easing function for the animation on the x axis /// - parameter easingOptionY: the easing function for the animation on the y axis @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOptionX: ChartEasingOption, easingOptionY: ChartEasingOption) { animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easingFunctionFromOption(easingOptionX), easingY: easingFunctionFromOption(easingOptionY)) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easing: an easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easing, easingY: easing) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOption: the easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine) { animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easing: easingFunctionFromOption(easingOption)) } /// Animates the drawing / rendering of the chart the x-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter easing: an easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { _startTimeX = CACurrentMediaTime() _durationX = xAxisDuration _endTimeX = _startTimeX + xAxisDuration _endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY _enabledX = xAxisDuration > 0.0 _easingX = easing // Take care of the first frame if rendering is already scheduled... updateAnimationPhases(_startTimeX) if _enabledX || _enabledY, _displayLink == nil { _displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop)) _displayLink?.add(to: .main, forMode: .common) } } /// Animates the drawing / rendering of the chart the x-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter easingOption: the easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine) { animate(xAxisDuration: xAxisDuration, easing: easingFunctionFromOption(easingOption)) } /// Animates the drawing / rendering of the chart the y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easing: an easing function for the animation @objc open func animate(yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { _startTimeY = CACurrentMediaTime() _durationY = yAxisDuration _endTimeY = _startTimeY + yAxisDuration _endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY _enabledY = yAxisDuration > 0.0 _easingY = easing // Take care of the first frame if rendering is already scheduled... updateAnimationPhases(_startTimeY) if _enabledX || _enabledY, _displayLink == nil { _displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop)) _displayLink?.add(to: .main, forMode: .common) } } /// Animates the drawing / rendering of the chart the y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOption: the easing function for the animation @objc open func animate(yAxisDuration: TimeInterval, easingOption: ChartEasingOption = .easeInOutSine) { animate(yAxisDuration: yAxisDuration, easing: easingFunctionFromOption(easingOption)) } } ================================================ FILE: Pods/Charts/Source/Charts/Animation/ChartAnimationEasing.swift ================================================ // // ChartAnimationUtils.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public enum ChartEasingOption: Int { case linear case easeInQuad case easeOutQuad case easeInOutQuad case easeInCubic case easeOutCubic case easeInOutCubic case easeInQuart case easeOutQuart case easeInOutQuart case easeInQuint case easeOutQuint case easeInOutQuint case easeInSine case easeOutSine case easeInOutSine case easeInExpo case easeOutExpo case easeInOutExpo case easeInCirc case easeOutCirc case easeInOutCirc case easeInElastic case easeOutElastic case easeInOutElastic case easeInBack case easeOutBack case easeInOutBack case easeInBounce case easeOutBounce case easeInOutBounce } public typealias ChartEasingFunctionBlock = ((_ elapsed: TimeInterval, _ duration: TimeInterval) -> Double) internal func easingFunctionFromOption(_ easing: ChartEasingOption) -> ChartEasingFunctionBlock { switch easing { case .linear: return EasingFunctions.Linear case .easeInQuad: return EasingFunctions.EaseInQuad case .easeOutQuad: return EasingFunctions.EaseOutQuad case .easeInOutQuad: return EasingFunctions.EaseInOutQuad case .easeInCubic: return EasingFunctions.EaseInCubic case .easeOutCubic: return EasingFunctions.EaseOutCubic case .easeInOutCubic: return EasingFunctions.EaseInOutCubic case .easeInQuart: return EasingFunctions.EaseInQuart case .easeOutQuart: return EasingFunctions.EaseOutQuart case .easeInOutQuart: return EasingFunctions.EaseInOutQuart case .easeInQuint: return EasingFunctions.EaseInQuint case .easeOutQuint: return EasingFunctions.EaseOutQuint case .easeInOutQuint: return EasingFunctions.EaseInOutQuint case .easeInSine: return EasingFunctions.EaseInSine case .easeOutSine: return EasingFunctions.EaseOutSine case .easeInOutSine: return EasingFunctions.EaseInOutSine case .easeInExpo: return EasingFunctions.EaseInExpo case .easeOutExpo: return EasingFunctions.EaseOutExpo case .easeInOutExpo: return EasingFunctions.EaseInOutExpo case .easeInCirc: return EasingFunctions.EaseInCirc case .easeOutCirc: return EasingFunctions.EaseOutCirc case .easeInOutCirc: return EasingFunctions.EaseInOutCirc case .easeInElastic: return EasingFunctions.EaseInElastic case .easeOutElastic: return EasingFunctions.EaseOutElastic case .easeInOutElastic: return EasingFunctions.EaseInOutElastic case .easeInBack: return EasingFunctions.EaseInBack case .easeOutBack: return EasingFunctions.EaseOutBack case .easeInOutBack: return EasingFunctions.EaseInOutBack case .easeInBounce: return EasingFunctions.EaseInBounce case .easeOutBounce: return EasingFunctions.EaseOutBounce case .easeInOutBounce: return EasingFunctions.EaseInOutBounce } } internal struct EasingFunctions { internal static let Linear = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in return Double(elapsed / duration) } internal static let EaseInQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return position * position } internal static let EaseOutQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return -position * (position - 2.0) } internal static let EaseInOutQuad = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / (duration / 2.0)) if position < 1.0 { return 0.5 * position * position } return -0.5 * ((position - 1.0) * (position - 3.0) - 1.0) } internal static let EaseInCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return position * position * position } internal static let EaseOutCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) position -= 1.0 return (position * position * position + 1.0) } internal static let EaseInOutCubic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / (duration / 2.0)) if position < 1.0 { return 0.5 * position * position * position } position -= 2.0 return 0.5 * (position * position * position + 2.0) } internal static let EaseInQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return position * position * position * position } internal static let EaseOutQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) position -= 1.0 return -(position * position * position * position - 1.0) } internal static let EaseInOutQuart = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / (duration / 2.0)) if position < 1.0 { return 0.5 * position * position * position * position } position -= 2.0 return -0.5 * (position * position * position * position - 2.0) } internal static let EaseInQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return position * position * position * position * position } internal static let EaseOutQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) position -= 1.0 return (position * position * position * position * position + 1.0) } internal static let EaseInOutQuint = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / (duration / 2.0)) if position < 1.0 { return 0.5 * position * position * position * position * position } else { position -= 2.0 return 0.5 * (position * position * position * position * position + 2.0) } } internal static let EaseInSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / duration return Double( -cos(position * Double.pi / 2) + 1.0 ) } internal static let EaseOutSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / duration return Double( sin(position * Double.pi / 2) ) } internal static let EaseInOutSine = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / duration return Double( -0.5 * (cos(Double.pi * position) - 1.0) ) } internal static let EaseInExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in return (elapsed == 0) ? 0.0 : Double(pow(2.0, 10.0 * (elapsed / duration - 1.0))) } internal static let EaseOutExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in return (elapsed == duration) ? 1.0 : (-Double(pow(2.0, -10.0 * elapsed / duration)) + 1.0) } internal static let EaseInOutExpo = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in if elapsed == 0 { return 0.0 } if elapsed == duration { return 1.0 } var position: TimeInterval = elapsed / (duration / 2.0) if position < 1.0 { return Double( 0.5 * pow(2.0, 10.0 * (position - 1.0)) ) } position = position - 1.0 return Double( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) ) } internal static let EaseInCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) return -(Double(sqrt(1.0 - position * position)) - 1.0) } internal static let EaseOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position = Double(elapsed / duration) position -= 1.0 return Double( sqrt(1 - position * position) ) } internal static let EaseInOutCirc = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / (duration / 2.0) if position < 1.0 { return Double( -0.5 * (sqrt(1.0 - position * position) - 1.0) ) } position -= 2.0 return Double( 0.5 * (sqrt(1.0 - position * position) + 1.0) ) } internal static let EaseInElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in if elapsed == 0.0 { return 0.0 } var position: TimeInterval = elapsed / duration if position == 1.0 { return 1.0 } var p = duration * 0.3 var s = p / (2.0 * Double.pi) * asin(1.0) position -= 1.0 return Double( -(pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) ) } internal static let EaseOutElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in if elapsed == 0.0 { return 0.0 } var position: TimeInterval = elapsed / duration if position == 1.0 { return 1.0 } var p = duration * 0.3 var s = p / (2.0 * Double.pi) * asin(1.0) return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) + 1.0 ) } internal static let EaseInOutElastic = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in if elapsed == 0.0 { return 0.0 } var position: TimeInterval = elapsed / (duration / 2.0) if position == 2.0 { return 1.0 } var p = duration * (0.3 * 1.5) var s = p / (2.0 * Double.pi) * asin(1.0) if position < 1.0 { position -= 1.0 return Double( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p)) ) } position -= 1.0 return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * Double.pi) / p) * 0.5 + 1.0 ) } internal static let EaseInBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in let s: TimeInterval = 1.70158 var position: TimeInterval = elapsed / duration return Double( position * position * ((s + 1.0) * position - s) ) } internal static let EaseOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in let s: TimeInterval = 1.70158 var position: TimeInterval = elapsed / duration position -= 1.0 return Double( position * position * ((s + 1.0) * position + s) + 1.0 ) } internal static let EaseInOutBack = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var s: TimeInterval = 1.70158 var position: TimeInterval = elapsed / (duration / 2.0) if position < 1.0 { s *= 1.525 return Double( 0.5 * (position * position * ((s + 1.0) * position - s)) ) } s *= 1.525 position -= 2.0 return Double( 0.5 * (position * position * ((s + 1.0) * position + s) + 2.0) ) } internal static let EaseInBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in return 1.0 - EaseOutBounce(duration - elapsed, duration) } internal static let EaseOutBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in var position: TimeInterval = elapsed / duration if position < (1.0 / 2.75) { return Double( 7.5625 * position * position ) } else if position < (2.0 / 2.75) { position -= (1.5 / 2.75) return Double( 7.5625 * position * position + 0.75 ) } else if position < (2.5 / 2.75) { position -= (2.25 / 2.75) return Double( 7.5625 * position * position + 0.9375 ) } else { position -= (2.625 / 2.75) return Double( 7.5625 * position * position + 0.984375 ) } } internal static let EaseInOutBounce = { (elapsed: TimeInterval, duration: TimeInterval) -> Double in if elapsed < (duration / 2.0) { return EaseInBounce(elapsed * 2.0, duration) * 0.5 } return EaseOutBounce(elapsed * 2.0 - duration, duration) * 0.5 + 0.5 } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/BarChartView.swift ================================================ // // BarChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Chart that draws bars. open class BarChartView: BarLineChartViewBase, BarChartDataProvider { /// if set to true, all values are drawn above their bars, instead of below their top private var _drawValueAboveBarEnabled = true /// if set to true, a grey area is drawn behind each bar that indicates the maximum value private var _drawBarShadowEnabled = false internal override func initialize() { super.initialize() renderer = BarChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) self.highlighter = BarHighlighter(chart: self) self.xAxis.spaceMin = 0.5 self.xAxis.spaceMax = 0.5 } internal override func calcMinMax() { guard let data = self.data as? BarChartData else { return } if fitBars { _xAxis.calculate( min: data.xMin - data.barWidth / 2.0, max: data.xMax + data.barWidth / 2.0) } else { _xAxis.calculate(min: data.xMin, max: data.xMax) } // calculate axis range (min / max) according to provided data leftAxis.calculate( min: data.getYMin(axis: .left), max: data.getYMax(axis: .left)) rightAxis.calculate( min: data.getYMin(axis: .right), max: data.getYMax(axis: .right)) } /// - returns: The Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the BarChart. open override func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight? { if _data === nil { Swift.print("Can't select by touch. No data set.") return nil } guard let h = self.highlighter?.getHighlight(x: pt.x, y: pt.y) else { return nil } if !isHighlightFullBarEnabled { return h } // For isHighlightFullBarEnabled, remove stackIndex return Highlight( x: h.x, y: h.y, xPx: h.xPx, yPx: h.yPx, dataIndex: h.dataIndex, dataSetIndex: h.dataSetIndex, stackIndex: -1, axis: h.axis) } /// - returns: The bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be found in the charts data. @objc open func getBarBounds(entry e: BarChartDataEntry) -> CGRect { guard let data = _data as? BarChartData, let set = data.getDataSetForEntry(e) as? IBarChartDataSet else { return CGRect.null } let y = e.y let x = e.x let barWidth = data.barWidth let left = x - barWidth / 2.0 let right = x + barWidth / 2.0 let top = y >= 0.0 ? y : 0.0 let bottom = y <= 0.0 ? y : 0.0 var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top) getTransformer(forAxis: set.axisDependency).rectValueToPixel(&bounds) return bounds } /// Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries. /// Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified by the parameters. /// Calls `notifyDataSetChanged()` afterwards. /// /// - parameter fromX: the starting point on the x-axis where the grouping should begin /// - parameter groupSpace: the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f /// - parameter barSpace: the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f @objc open func groupBars(fromX: Double, groupSpace: Double, barSpace: Double) { guard let barData = self.barData else { Swift.print("You need to set data for the chart before grouping bars.", terminator: "\n") return } barData.groupBars(fromX: fromX, groupSpace: groupSpace, barSpace: barSpace) notifyDataSetChanged() } /// Highlights the value at the given x-value in the given DataSet. Provide -1 as the dataSetIndex to undo all highlighting. /// - parameter x: /// - parameter dataSetIndex: /// - parameter stackIndex: the index inside the stack - only relevant for stacked entries @objc open func highlightValue(x: Double, dataSetIndex: Int, stackIndex: Int) { highlightValue(Highlight(x: x, dataSetIndex: dataSetIndex, stackIndex: stackIndex)) } // MARK: Accessors /// if set to true, all values are drawn above their bars, instead of below their top @objc open var drawValueAboveBarEnabled: Bool { get { return _drawValueAboveBarEnabled } set { _drawValueAboveBarEnabled = newValue setNeedsDisplay() } } /// if set to true, a grey area is drawn behind each bar that indicates the maximum value @objc open var drawBarShadowEnabled: Bool { get { return _drawBarShadowEnabled } set { _drawBarShadowEnabled = newValue setNeedsDisplay() } } /// Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be fully displayed. /// **default**: false @objc open var fitBars = false /// Set this to `true` to make the highlight operation full-bar oriented, `false` to make it highlight single values (relevant only for stacked). /// If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry was tapped. @objc open var highlightFullBarEnabled: Bool = false /// - returns: `true` the highlight is be full-bar oriented, `false` ifsingle-value open var isHighlightFullBarEnabled: Bool { return highlightFullBarEnabled } // MARK: - BarChartDataProvider open var barData: BarChartData? { return _data as? BarChartData } /// - returns: `true` if drawing values above bars is enabled, `false` ifnot open var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled } /// - returns: `true` if drawing shadows (maxvalue) for each bar is enabled, `false` ifnot open var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/BarLineChartViewBase.swift ================================================ // // BarLineChartViewBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// Base-class of LineChart, BarChart, ScatterChart and CandleStickChart. open class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChartDataProvider, NSUIGestureRecognizerDelegate { /// the maximum number of entries to which values will be drawn /// (entry numbers greater than this value will cause value-labels to disappear) internal var _maxVisibleCount = 100 /// flag that indicates if auto scaling on the y axis is enabled private var _autoScaleMinMaxEnabled = false private var _pinchZoomEnabled = false private var _doubleTapToZoomEnabled = true private var _dragXEnabled = true private var _dragYEnabled = true private var _scaleXEnabled = true private var _scaleYEnabled = true /// the color for the background of the chart-drawing area (everything behind the grid lines). @objc open var gridBackgroundColor = NSUIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0) @objc open var borderColor = NSUIColor.black @objc open var borderLineWidth: CGFloat = 1.0 /// flag indicating if the grid background should be drawn or not @objc open var drawGridBackgroundEnabled = false /// When enabled, the borders rectangle will be rendered. /// If this is enabled, there is no point drawing the axis-lines of x- and y-axis. @objc open var drawBordersEnabled = false /// When enabled, the values will be clipped to contentRect, otherwise they can bleed outside the content rect. @objc open var clipValuesToContentEnabled: Bool = false /// When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can /// be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines) /// that there is unwanted clipping. @objc open var clipDataToContentEnabled: Bool = true /// Sets the minimum offset (padding) around the chart, defaults to 10 @objc open var minOffset = CGFloat(10.0) /// Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change) /// **default**: false @objc open var keepPositionOnRotation: Bool = false /// The left y-axis object. In the horizontal bar-chart, this is the /// top axis. @objc open internal(set) var leftAxis = YAxis(position: .left) /// The right y-axis object. In the horizontal bar-chart, this is the /// bottom axis. @objc open internal(set) var rightAxis = YAxis(position: .right) /// The left Y axis renderer. This is a read-write property so you can set your own custom renderer here. /// **default**: An instance of YAxisRenderer @objc open lazy var leftYAxisRenderer = YAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: leftAxis, transformer: _leftAxisTransformer) /// The right Y axis renderer. This is a read-write property so you can set your own custom renderer here. /// **default**: An instance of YAxisRenderer @objc open lazy var rightYAxisRenderer = YAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: rightAxis, transformer: _rightAxisTransformer) internal var _leftAxisTransformer: Transformer! internal var _rightAxisTransformer: Transformer! /// The X axis renderer. This is a read-write property so you can set your own custom renderer here. /// **default**: An instance of XAxisRenderer @objc open lazy var xAxisRenderer = XAxisRenderer(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer) internal var _tapGestureRecognizer: NSUITapGestureRecognizer! internal var _doubleTapGestureRecognizer: NSUITapGestureRecognizer! #if !os(tvOS) internal var _pinchGestureRecognizer: NSUIPinchGestureRecognizer! #endif internal var _panGestureRecognizer: NSUIPanGestureRecognizer! /// flag that indicates if a custom viewport offset has been set private var _customViewPortEnabled = false public override init(frame: CGRect) { super.init(frame: frame) } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } deinit { stopDeceleration() } internal override func initialize() { super.initialize() _leftAxisTransformer = Transformer(viewPortHandler: _viewPortHandler) _rightAxisTransformer = Transformer(viewPortHandler: _viewPortHandler) self.highlighter = ChartHighlighter(chart: self) _tapGestureRecognizer = NSUITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))) _doubleTapGestureRecognizer = NSUITapGestureRecognizer(target: self, action: #selector(doubleTapGestureRecognized(_:))) _doubleTapGestureRecognizer.nsuiNumberOfTapsRequired = 2 _panGestureRecognizer = NSUIPanGestureRecognizer(target: self, action: #selector(panGestureRecognized(_:))) _panGestureRecognizer.delegate = self self.addGestureRecognizer(_tapGestureRecognizer) self.addGestureRecognizer(_doubleTapGestureRecognizer) self.addGestureRecognizer(_panGestureRecognizer) _doubleTapGestureRecognizer.isEnabled = _doubleTapToZoomEnabled _panGestureRecognizer.isEnabled = _dragXEnabled || _dragYEnabled #if !os(tvOS) _pinchGestureRecognizer = NSUIPinchGestureRecognizer(target: self, action: #selector(BarLineChartViewBase.pinchGestureRecognized(_:))) _pinchGestureRecognizer.delegate = self self.addGestureRecognizer(_pinchGestureRecognizer) _pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled #endif } open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // Saving current position of chart. var oldPoint: CGPoint? if (keepPositionOnRotation && (keyPath == "frame" || keyPath == "bounds")) { oldPoint = viewPortHandler.contentRect.origin getTransformer(forAxis: .left).pixelToValues(&oldPoint!) } // Superclass transforms chart. super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) // Restoring old position of chart if var newPoint = oldPoint , keepPositionOnRotation { getTransformer(forAxis: .left).pointValueToPixel(&newPoint) viewPortHandler.centerViewPort(pt: newPoint, chart: self) } else { viewPortHandler.refresh(newMatrix: viewPortHandler.touchMatrix, chart: self, invalidate: true) } } open override func draw(_ rect: CGRect) { super.draw(rect) guard data != nil, let renderer = renderer else { return } let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext else { return } // execute all drawing commands drawGridBackground(context: context) if _autoScaleMinMaxEnabled { autoScale() } if leftAxis.isEnabled { leftYAxisRenderer.computeAxis(min: leftAxis._axisMinimum, max: leftAxis._axisMaximum, inverted: leftAxis.isInverted) } if rightAxis.isEnabled { rightYAxisRenderer.computeAxis(min: rightAxis._axisMinimum, max: rightAxis._axisMaximum, inverted: rightAxis.isInverted) } if _xAxis.isEnabled { xAxisRenderer.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) } xAxisRenderer.renderAxisLine(context: context) leftYAxisRenderer.renderAxisLine(context: context) rightYAxisRenderer.renderAxisLine(context: context) // The renderers are responsible for clipping, to account for line-width center etc. xAxisRenderer.renderGridLines(context: context) leftYAxisRenderer.renderGridLines(context: context) rightYAxisRenderer.renderGridLines(context: context) if _xAxis.isEnabled && _xAxis.isDrawLimitLinesBehindDataEnabled { xAxisRenderer.renderLimitLines(context: context) } if leftAxis.isEnabled && leftAxis.isDrawLimitLinesBehindDataEnabled { leftYAxisRenderer.renderLimitLines(context: context) } if rightAxis.isEnabled && rightAxis.isDrawLimitLinesBehindDataEnabled { rightYAxisRenderer.renderLimitLines(context: context) } context.saveGState() // make sure the data cannot be drawn outside the content-rect if clipDataToContentEnabled { context.clip(to: _viewPortHandler.contentRect) } renderer.drawData(context: context) // if highlighting is enabled if (valuesToHighlight()) { renderer.drawHighlighted(context: context, indices: _indicesToHighlight) } context.restoreGState() renderer.drawExtras(context: context) if _xAxis.isEnabled && !_xAxis.isDrawLimitLinesBehindDataEnabled { xAxisRenderer.renderLimitLines(context: context) } if leftAxis.isEnabled && !leftAxis.isDrawLimitLinesBehindDataEnabled { leftYAxisRenderer.renderLimitLines(context: context) } if rightAxis.isEnabled && !rightAxis.isDrawLimitLinesBehindDataEnabled { rightYAxisRenderer.renderLimitLines(context: context) } xAxisRenderer.renderAxisLabels(context: context) leftYAxisRenderer.renderAxisLabels(context: context) rightYAxisRenderer.renderAxisLabels(context: context) if clipValuesToContentEnabled { context.saveGState() context.clip(to: _viewPortHandler.contentRect) renderer.drawValues(context: context) context.restoreGState() } else { renderer.drawValues(context: context) } _legendRenderer.renderLegend(context: context) drawDescription(context: context) drawMarkers(context: context) } private var _autoScaleLastLowestVisibleX: Double? private var _autoScaleLastHighestVisibleX: Double? /// Performs auto scaling of the axis by recalculating the minimum and maximum y-values based on the entries currently in view. internal func autoScale() { guard let data = _data else { return } data.calcMinMaxY(fromX: self.lowestVisibleX, toX: self.highestVisibleX) _xAxis.calculate(min: data.xMin, max: data.xMax) // calculate axis range (min / max) according to provided data if leftAxis.isEnabled { leftAxis.calculate(min: data.getYMin(axis: .left), max: data.getYMax(axis: .left)) } if rightAxis.isEnabled { rightAxis.calculate(min: data.getYMin(axis: .right), max: data.getYMax(axis: .right)) } calculateOffsets() } internal func prepareValuePxMatrix() { _rightAxisTransformer.prepareMatrixValuePx(chartXMin: _xAxis._axisMinimum, deltaX: CGFloat(xAxis.axisRange), deltaY: CGFloat(rightAxis.axisRange), chartYMin: rightAxis._axisMinimum) _leftAxisTransformer.prepareMatrixValuePx(chartXMin: xAxis._axisMinimum, deltaX: CGFloat(xAxis.axisRange), deltaY: CGFloat(leftAxis.axisRange), chartYMin: leftAxis._axisMinimum) } internal func prepareOffsetMatrix() { _rightAxisTransformer.prepareMatrixOffset(inverted: rightAxis.isInverted) _leftAxisTransformer.prepareMatrixOffset(inverted: leftAxis.isInverted) } open override func notifyDataSetChanged() { renderer?.initBuffers() calcMinMax() leftYAxisRenderer.computeAxis(min: leftAxis._axisMinimum, max: leftAxis._axisMaximum, inverted: leftAxis.isInverted) rightYAxisRenderer.computeAxis(min: rightAxis._axisMinimum, max: rightAxis._axisMaximum, inverted: rightAxis.isInverted) if let data = _data { xAxisRenderer.computeAxis( min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) if _legend !== nil { legendRenderer?.computeLegend(data: data) } } calculateOffsets() setNeedsDisplay() } internal override func calcMinMax() { // calculate / set x-axis range _xAxis.calculate(min: _data?.xMin ?? 0.0, max: _data?.xMax ?? 0.0) // calculate axis range (min / max) according to provided data leftAxis.calculate(min: _data?.getYMin(axis: .left) ?? 0.0, max: _data?.getYMax(axis: .left) ?? 0.0) rightAxis.calculate(min: _data?.getYMin(axis: .right) ?? 0.0, max: _data?.getYMax(axis: .right) ?? 0.0) } internal func calculateLegendOffsets(offsetLeft: inout CGFloat, offsetTop: inout CGFloat, offsetRight: inout CGFloat, offsetBottom: inout CGFloat) { // setup offsets for legend if _legend !== nil && _legend.isEnabled && !_legend.drawInside { switch _legend.orientation { case .vertical: switch _legend.horizontalAlignment { case .left: offsetLeft += min(_legend.neededWidth, _viewPortHandler.chartWidth * _legend.maxSizePercent) + _legend.xOffset case .right: offsetRight += min(_legend.neededWidth, _viewPortHandler.chartWidth * _legend.maxSizePercent) + _legend.xOffset case .center: switch _legend.verticalAlignment { case .top: offsetTop += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset case .bottom: offsetBottom += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset default: break } } case .horizontal: switch _legend.verticalAlignment { case .top: offsetTop += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset if xAxis.isEnabled && xAxis.isDrawLabelsEnabled { offsetTop += xAxis.labelRotatedHeight } case .bottom: offsetBottom += min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) + _legend.yOffset if xAxis.isEnabled && xAxis.isDrawLabelsEnabled { offsetBottom += xAxis.labelRotatedHeight } default: break } } } } internal override func calculateOffsets() { if !_customViewPortEnabled { var offsetLeft = CGFloat(0.0) var offsetRight = CGFloat(0.0) var offsetTop = CGFloat(0.0) var offsetBottom = CGFloat(0.0) calculateLegendOffsets(offsetLeft: &offsetLeft, offsetTop: &offsetTop, offsetRight: &offsetRight, offsetBottom: &offsetBottom) // offsets for y-labels if leftAxis.needsOffset { offsetLeft += leftAxis.requiredSize().width } if rightAxis.needsOffset { offsetRight += rightAxis.requiredSize().width } if xAxis.isEnabled && xAxis.isDrawLabelsEnabled { let xlabelheight = xAxis.labelRotatedHeight + xAxis.yOffset // offsets for x-labels if xAxis.labelPosition == .bottom { offsetBottom += xlabelheight } else if xAxis.labelPosition == .top { offsetTop += xlabelheight } else if xAxis.labelPosition == .bothSided { offsetBottom += xlabelheight offsetTop += xlabelheight } } offsetTop += self.extraTopOffset offsetRight += self.extraRightOffset offsetBottom += self.extraBottomOffset offsetLeft += self.extraLeftOffset _viewPortHandler.restrainViewPort( offsetLeft: max(self.minOffset, offsetLeft), offsetTop: max(self.minOffset, offsetTop), offsetRight: max(self.minOffset, offsetRight), offsetBottom: max(self.minOffset, offsetBottom)) } prepareOffsetMatrix() prepareValuePxMatrix() } /// draws the grid background internal func drawGridBackground(context: CGContext) { if drawGridBackgroundEnabled || drawBordersEnabled { context.saveGState() } if drawGridBackgroundEnabled { // draw the grid background context.setFillColor(gridBackgroundColor.cgColor) context.fill(_viewPortHandler.contentRect) } if drawBordersEnabled { context.setLineWidth(borderLineWidth) context.setStrokeColor(borderColor.cgColor) context.stroke(_viewPortHandler.contentRect) } if drawGridBackgroundEnabled || drawBordersEnabled { context.restoreGState() } } // MARK: - Gestures private enum GestureScaleAxis { case both case x case y } private var _isDragging = false private var _isScaling = false private var _gestureScaleAxis = GestureScaleAxis.both private var _closestDataSetToTouch: IChartDataSet! private var _panGestureReachedEdge: Bool = false private weak var _outerScrollView: NSUIScrollView? private var _lastPanPoint = CGPoint() /// This is to prevent using setTranslation which resets velocity private var _decelerationLastTime: TimeInterval = 0.0 private var _decelerationDisplayLink: NSUIDisplayLink! private var _decelerationVelocity = CGPoint() @objc private func tapGestureRecognized(_ recognizer: NSUITapGestureRecognizer) { if _data === nil { return } if recognizer.state == NSUIGestureRecognizerState.ended { if !isHighLightPerTapEnabled { return } let h = getHighlightByTouchPoint(recognizer.location(in: self)) if h === nil || h == self.lastHighlighted { lastHighlighted = nil highlightValue(nil, callDelegate: true) } else { lastHighlighted = h highlightValue(h, callDelegate: true) } } } @objc private func doubleTapGestureRecognized(_ recognizer: NSUITapGestureRecognizer) { if _data === nil { return } if recognizer.state == NSUIGestureRecognizerState.ended { if _data !== nil && _doubleTapToZoomEnabled && (data?.entryCount ?? 0) > 0 { var location = recognizer.location(in: self) location.x = location.x - _viewPortHandler.offsetLeft if isTouchInverted() { location.y = -(location.y - _viewPortHandler.offsetTop) } else { location.y = -(self.bounds.size.height - location.y - _viewPortHandler.offsetBottom) } self.zoom(scaleX: isScaleXEnabled ? 1.4 : 1.0, scaleY: isScaleYEnabled ? 1.4 : 1.0, x: location.x, y: location.y) } } } #if !os(tvOS) @objc private func pinchGestureRecognized(_ recognizer: NSUIPinchGestureRecognizer) { if recognizer.state == NSUIGestureRecognizerState.began { stopDeceleration() if _data !== nil && (_pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled) { _isScaling = true if _pinchZoomEnabled { _gestureScaleAxis = .both } else { let x = abs(recognizer.location(in: self).x - recognizer.nsuiLocationOfTouch(1, inView: self).x) let y = abs(recognizer.location(in: self).y - recognizer.nsuiLocationOfTouch(1, inView: self).y) if _scaleXEnabled != _scaleYEnabled { _gestureScaleAxis = _scaleXEnabled ? .x : .y } else { _gestureScaleAxis = x > y ? .x : .y } } } } else if recognizer.state == NSUIGestureRecognizerState.ended || recognizer.state == NSUIGestureRecognizerState.cancelled { if _isScaling { _isScaling = false // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } } else if recognizer.state == NSUIGestureRecognizerState.changed { let isZoomingOut = (recognizer.nsuiScale < 1) var canZoomMoreX = isZoomingOut ? _viewPortHandler.canZoomOutMoreX : _viewPortHandler.canZoomInMoreX var canZoomMoreY = isZoomingOut ? _viewPortHandler.canZoomOutMoreY : _viewPortHandler.canZoomInMoreY if _isScaling { canZoomMoreX = canZoomMoreX && _scaleXEnabled && (_gestureScaleAxis == .both || _gestureScaleAxis == .x) canZoomMoreY = canZoomMoreY && _scaleYEnabled && (_gestureScaleAxis == .both || _gestureScaleAxis == .y) if canZoomMoreX || canZoomMoreY { var location = recognizer.location(in: self) location.x = location.x - _viewPortHandler.offsetLeft if isTouchInverted() { location.y = -(location.y - _viewPortHandler.offsetTop) } else { location.y = -(_viewPortHandler.chartHeight - location.y - _viewPortHandler.offsetBottom) } let scaleX = canZoomMoreX ? recognizer.nsuiScale : 1.0 let scaleY = canZoomMoreY ? recognizer.nsuiScale : 1.0 var matrix = CGAffineTransform(translationX: location.x, y: location.y) matrix = matrix.scaledBy(x: scaleX, y: scaleY) matrix = matrix.translatedBy(x: -location.x, y: -location.y) matrix = _viewPortHandler.touchMatrix.concatenating(matrix) _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true) if delegate !== nil { delegate?.chartScaled?(self, scaleX: scaleX, scaleY: scaleY) } } recognizer.nsuiScale = 1.0 } } } #endif @objc private func panGestureRecognized(_ recognizer: NSUIPanGestureRecognizer) { if recognizer.state == NSUIGestureRecognizerState.began && recognizer.nsuiNumberOfTouches() > 0 { stopDeceleration() if _data === nil || !self.isDragEnabled { // If we have no data, we have nothing to pan and no data to highlight return } // If drag is enabled and we are in a position where there's something to drag: // * If we're zoomed in, then obviously we have something to drag. // * If we have a drag offset - we always have something to drag if !self.hasNoDragOffset || !self.isFullyZoomedOut { _isDragging = true _closestDataSetToTouch = getDataSetByTouchPoint(point: recognizer.nsuiLocationOfTouch(0, inView: self)) var translation = recognizer.translation(in: self) if !self.dragXEnabled { translation.x = 0.0 } else if !self.dragYEnabled { translation.y = 0.0 } let didUserDrag = translation.x != 0.0 || translation.y != 0.0 // Check to see if user dragged at all and if so, can the chart be dragged by the given amount if didUserDrag && !performPanChange(translation: translation) { if _outerScrollView !== nil { // We can stop dragging right now, and let the scroll view take control _outerScrollView = nil _isDragging = false } } else { if _outerScrollView !== nil { // Prevent the parent scroll view from scrolling _outerScrollView?.nsuiIsScrollEnabled = false } } _lastPanPoint = recognizer.translation(in: self) } else if self.isHighlightPerDragEnabled { // We will only handle highlights on NSUIGestureRecognizerState.Changed _isDragging = false } } else if recognizer.state == NSUIGestureRecognizerState.changed { if _isDragging { let originalTranslation = recognizer.translation(in: self) var translation = CGPoint(x: originalTranslation.x - _lastPanPoint.x, y: originalTranslation.y - _lastPanPoint.y) if !self.dragXEnabled { translation.x = 0.0 } else if !self.dragYEnabled { translation.y = 0.0 } let _ = performPanChange(translation: translation) _lastPanPoint = originalTranslation } else if isHighlightPerDragEnabled { let h = getHighlightByTouchPoint(recognizer.location(in: self)) let lastHighlighted = self.lastHighlighted if h != lastHighlighted { self.lastHighlighted = h self.highlightValue(h, callDelegate: true) } } } else if recognizer.state == NSUIGestureRecognizerState.ended || recognizer.state == NSUIGestureRecognizerState.cancelled { if _isDragging { if recognizer.state == NSUIGestureRecognizerState.ended && isDragDecelerationEnabled { stopDeceleration() _decelerationLastTime = CACurrentMediaTime() _decelerationVelocity = recognizer.velocity(in: self) _decelerationDisplayLink = NSUIDisplayLink(target: self, selector: #selector(BarLineChartViewBase.decelerationLoop)) _decelerationDisplayLink.add(to: RunLoop.main, forMode: .common) } _isDragging = false } if _outerScrollView !== nil { _outerScrollView?.nsuiIsScrollEnabled = true _outerScrollView = nil } } } private func performPanChange(translation: CGPoint) -> Bool { var translation = translation if isTouchInverted() { if self is HorizontalBarChartView { translation.x = -translation.x } else { translation.y = -translation.y } } let originalMatrix = _viewPortHandler.touchMatrix var matrix = CGAffineTransform(translationX: translation.x, y: translation.y) matrix = originalMatrix.concatenating(matrix) matrix = _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true) if delegate !== nil { delegate?.chartTranslated?(self, dX: translation.x, dY: translation.y) } // Did we managed to actually drag or did we reach the edge? return matrix.tx != originalMatrix.tx || matrix.ty != originalMatrix.ty } private func isTouchInverted() -> Bool { return isAnyAxisInverted && _closestDataSetToTouch !== nil && getAxis(_closestDataSetToTouch.axisDependency).isInverted } @objc open func stopDeceleration() { if _decelerationDisplayLink !== nil { _decelerationDisplayLink.remove(from: RunLoop.main, forMode: .common) _decelerationDisplayLink = nil } } @objc private func decelerationLoop() { let currentTime = CACurrentMediaTime() _decelerationVelocity.x *= self.dragDecelerationFrictionCoef _decelerationVelocity.y *= self.dragDecelerationFrictionCoef let timeInterval = CGFloat(currentTime - _decelerationLastTime) let distance = CGPoint( x: _decelerationVelocity.x * timeInterval, y: _decelerationVelocity.y * timeInterval ) if !performPanChange(translation: distance) { // We reached the edge, stop _decelerationVelocity.x = 0.0 _decelerationVelocity.y = 0.0 } _decelerationLastTime = currentTime if abs(_decelerationVelocity.x) < 0.001 && abs(_decelerationVelocity.y) < 0.001 { stopDeceleration() // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } } private func nsuiGestureRecognizerShouldBegin(_ gestureRecognizer: NSUIGestureRecognizer) -> Bool { if gestureRecognizer == _panGestureRecognizer { let velocity = _panGestureRecognizer.velocity(in: self) if _data === nil || !isDragEnabled || (self.hasNoDragOffset && self.isFullyZoomedOut && !self.isHighlightPerDragEnabled) || (!_dragYEnabled && fabs(velocity.y) > fabs(velocity.x)) || (!_dragXEnabled && fabs(velocity.y) < fabs(velocity.x)) { return false } } else { #if !os(tvOS) if gestureRecognizer == _pinchGestureRecognizer { if _data === nil || (!_pinchZoomEnabled && !_scaleXEnabled && !_scaleYEnabled) { return false } } #endif } return true } #if !os(OSX) open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if !super.gestureRecognizerShouldBegin(gestureRecognizer) { return false } return nsuiGestureRecognizerShouldBegin(gestureRecognizer) } #endif #if os(OSX) public func gestureRecognizerShouldBegin(gestureRecognizer: NSGestureRecognizer) -> Bool { return nsuiGestureRecognizerShouldBegin(gestureRecognizer) } #endif open func gestureRecognizer(_ gestureRecognizer: NSUIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: NSUIGestureRecognizer) -> Bool { #if !os(tvOS) if ((gestureRecognizer is NSUIPinchGestureRecognizer && otherGestureRecognizer is NSUIPanGestureRecognizer) || (gestureRecognizer is NSUIPanGestureRecognizer && otherGestureRecognizer is NSUIPinchGestureRecognizer)) { return true } #endif if gestureRecognizer is NSUIPanGestureRecognizer, otherGestureRecognizer is NSUIPanGestureRecognizer, gestureRecognizer == _panGestureRecognizer { var scrollView = self.superview while scrollView != nil && !(scrollView is NSUIScrollView) { scrollView = scrollView?.superview } // If there is two scrollview together, we pick the superview of the inner scrollview. // In the case of UITableViewWrepperView, the superview will be UITableView if let superViewOfScrollView = scrollView?.superview, superViewOfScrollView is NSUIScrollView { scrollView = superViewOfScrollView } var foundScrollView = scrollView as? NSUIScrollView if !(foundScrollView?.nsuiIsScrollEnabled ?? true) { foundScrollView = nil } let scrollViewPanGestureRecognizer = foundScrollView?.nsuiGestureRecognizers?.first { $0 is NSUIPanGestureRecognizer } if otherGestureRecognizer === scrollViewPanGestureRecognizer { _outerScrollView = foundScrollView return true } } return false } /// MARK: Viewport modifiers /// Zooms in by 1.4, into the charts center. @objc open func zoomIn() { let center = _viewPortHandler.contentCenter let matrix = _viewPortHandler.zoomIn(x: center.x, y: -center.y) _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } /// Zooms out by 0.7, from the charts center. @objc open func zoomOut() { let center = _viewPortHandler.contentCenter let matrix = _viewPortHandler.zoomOut(x: center.x, y: -center.y) _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } /// Zooms out to original size. @objc open func resetZoom() { let matrix = _viewPortHandler.resetZoom() _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } /// Zooms in or out by the given scale factor. x and y are the coordinates /// (in pixels) of the zoom center. /// /// - parameter scaleX: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter x: /// - parameter y: @objc open func zoom( scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) { let matrix = _viewPortHandler.zoom(scaleX: scaleX, scaleY: scaleY, x: x, y: -y) _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) // Range might have changed, which means that Y-axis labels could have changed in size, affecting Y-axis size. So we need to recalculate offsets. calculateOffsets() setNeedsDisplay() } /// Zooms in or out by the given scale factor. /// x and y are the values (**not pixels**) of the zoom center. /// /// - parameter scaleX: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter xValue: /// - parameter yValue: /// - parameter axis: @objc open func zoom( scaleX: CGFloat, scaleY: CGFloat, xValue: Double, yValue: Double, axis: YAxis.AxisDependency) { let job = ZoomViewJob( viewPortHandler: viewPortHandler, scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, transformer: getTransformer(forAxis: axis), axis: axis, view: self) addViewportJob(job) } /// Zooms to the center of the chart with the given scale factor. /// /// - parameter scaleX: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter xValue: /// - parameter yValue: /// - parameter axis: @objc open func zoomToCenter( scaleX: CGFloat, scaleY: CGFloat) { let center = centerOffsets let matrix = viewPortHandler.zoom( scaleX: scaleX, scaleY: scaleY, x: center.x, y: -center.y) viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) } /// Zooms by the specified scale factor to the specified values on the specified axis. /// /// - parameter scaleX: /// - parameter scaleY: /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func zoomAndCenterViewAnimated( scaleX: CGFloat, scaleY: CGFloat, xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easing: ChartEasingFunctionBlock?) { let origin = valueForTouchPoint( point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) let job = AnimatedZoomViewJob( viewPortHandler: viewPortHandler, transformer: getTransformer(forAxis: axis), view: self, yAxis: getAxis(axis), xAxisRange: _xAxis.axisRange, scaleX: scaleX, scaleY: scaleY, xOrigin: viewPortHandler.scaleX, yOrigin: viewPortHandler.scaleY, zoomCenterX: CGFloat(xValue), zoomCenterY: CGFloat(yValue), zoomOriginX: origin.x, zoomOriginY: origin.y, duration: duration, easing: easing) addViewportJob(job) } /// Zooms by the specified scale factor to the specified values on the specified axis. /// /// - parameter scaleX: /// - parameter scaleY: /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func zoomAndCenterViewAnimated( scaleX: CGFloat, scaleY: CGFloat, xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easingOption: ChartEasingOption) { zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// Zooms by the specified scale factor to the specified values on the specified axis. /// /// - parameter scaleX: /// - parameter scaleY: /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func zoomAndCenterViewAnimated( scaleX: CGFloat, scaleY: CGFloat, xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval) { zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine) } /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. @objc open func fitScreen() { let matrix = _viewPortHandler.fitScreen() _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) calculateOffsets() setNeedsDisplay() } /// Sets the minimum scale value to which can be zoomed out. 1 = fitScreen @objc open func setScaleMinima(_ scaleX: CGFloat, scaleY: CGFloat) { _viewPortHandler.setMinimumScaleX(scaleX) _viewPortHandler.setMinimumScaleY(scaleY) } @objc open var visibleXRange: Double { return abs(highestVisibleX - lowestVisibleX) } /// Sets the size of the area (range on the x-axis) that should be maximum visible at once (no further zooming out allowed). /// /// If this is e.g. set to 10, no more than a range of 10 values on the x-axis can be viewed at once without scrolling. /// /// If you call this method, chart must have data or it has no effect. @objc open func setVisibleXRangeMaximum(_ maxXRange: Double) { let xScale = _xAxis.axisRange / maxXRange _viewPortHandler.setMinimumScaleX(CGFloat(xScale)) } /// Sets the size of the area (range on the x-axis) that should be minimum visible at once (no further zooming in allowed). /// /// If this is e.g. set to 10, no less than a range of 10 values on the x-axis can be viewed at once without scrolling. /// /// If you call this method, chart must have data or it has no effect. @objc open func setVisibleXRangeMinimum(_ minXRange: Double) { let xScale = _xAxis.axisRange / minXRange _viewPortHandler.setMaximumScaleX(CGFloat(xScale)) } /// Limits the maximum and minimum value count that can be visible by pinching and zooming. /// /// e.g. minRange=10, maxRange=100 no less than 10 values and no more that 100 values can be viewed /// at once without scrolling. /// /// If you call this method, chart must have data or it has no effect. @objc open func setVisibleXRange(minXRange: Double, maxXRange: Double) { let minScale = _xAxis.axisRange / maxXRange let maxScale = _xAxis.axisRange / minXRange _viewPortHandler.setMinMaxScaleX( minScaleX: CGFloat(minScale), maxScaleX: CGFloat(maxScale)) } /// Sets the size of the area (range on the y-axis) that should be maximum visible at once. /// /// - parameter yRange: /// - parameter axis: - the axis for which this limit should apply @objc open func setVisibleYRangeMaximum(_ maxYRange: Double, axis: YAxis.AxisDependency) { let yScale = getAxisRange(axis: axis) / maxYRange _viewPortHandler.setMinimumScaleY(CGFloat(yScale)) } /// Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. /// /// - parameter yRange: /// - parameter axis: - the axis for which this limit should apply @objc open func setVisibleYRangeMinimum(_ minYRange: Double, axis: YAxis.AxisDependency) { let yScale = getAxisRange(axis: axis) / minYRange _viewPortHandler.setMaximumScaleY(CGFloat(yScale)) } /// Limits the maximum and minimum y range that can be visible by pinching and zooming. /// /// - parameter minYRange: /// - parameter maxYRange: /// - parameter axis: @objc open func setVisibleYRange(minYRange: Double, maxYRange: Double, axis: YAxis.AxisDependency) { let minScale = getAxisRange(axis: axis) / minYRange let maxScale = getAxisRange(axis: axis) / maxYRange _viewPortHandler.setMinMaxScaleY(minScaleY: CGFloat(minScale), maxScaleY: CGFloat(maxScale)) } /// Moves the left side of the current viewport to the specified x-value. /// This also refreshes the chart by calling setNeedsDisplay(). @objc open func moveViewToX(_ xValue: Double) { let job = MoveViewJob( viewPortHandler: viewPortHandler, xValue: xValue, yValue: 0.0, transformer: getTransformer(forAxis: .left), view: self) addViewportJob(job) } /// Centers the viewport to the specified y-value on the y-axis. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis @objc open func moveViewToY(_ yValue: Double, axis: YAxis.AxisDependency) { let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY) let job = MoveViewJob( viewPortHandler: viewPortHandler, xValue: 0.0, yValue: yValue + yInView / 2.0, transformer: getTransformer(forAxis: axis), view: self) addViewportJob(job) } /// This will move the left side of the current viewport to the specified x-value on the x-axis, and center the viewport to the specified y-value on the y-axis. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis @objc open func moveViewTo(xValue: Double, yValue: Double, axis: YAxis.AxisDependency) { let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY) let job = MoveViewJob( viewPortHandler: viewPortHandler, xValue: xValue, yValue: yValue + yInView / 2.0, transformer: getTransformer(forAxis: axis), view: self) addViewportJob(job) } /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func moveViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easing: ChartEasingFunctionBlock?) { let bounds = valueForTouchPoint( point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY) let job = AnimatedMoveViewJob( viewPortHandler: viewPortHandler, xValue: xValue, yValue: yValue + yInView / 2.0, transformer: getTransformer(forAxis: axis), view: self, xOrigin: bounds.x, yOrigin: bounds.y, duration: duration, easing: easing) addViewportJob(job) } /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func moveViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easingOption: ChartEasingOption) { moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func moveViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval) { moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine) } /// This will move the center of the current viewport to the specified x-value and y-value. /// This also refreshes the chart by calling setNeedsDisplay(). /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis @objc open func centerViewTo( xValue: Double, yValue: Double, axis: YAxis.AxisDependency) { let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY) let xInView = xAxis.axisRange / Double(_viewPortHandler.scaleX) let job = MoveViewJob( viewPortHandler: viewPortHandler, xValue: xValue - xInView / 2.0, yValue: yValue + yInView / 2.0, transformer: getTransformer(forAxis: axis), view: self) addViewportJob(job) } /// This will move the center of the current viewport to the specified x-value and y-value animated. /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func centerViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easing: ChartEasingFunctionBlock?) { let bounds = valueForTouchPoint( point: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) let yInView = getAxisRange(axis: axis) / Double(_viewPortHandler.scaleY) let xInView = xAxis.axisRange / Double(_viewPortHandler.scaleX) let job = AnimatedMoveViewJob( viewPortHandler: viewPortHandler, xValue: xValue - xInView / 2.0, yValue: yValue + yInView / 2.0, transformer: getTransformer(forAxis: axis), view: self, xOrigin: bounds.x, yOrigin: bounds.y, duration: duration, easing: easing) addViewportJob(job) } /// This will move the center of the current viewport to the specified x-value and y-value animated. /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func centerViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval, easingOption: ChartEasingOption) { centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// This will move the center of the current viewport to the specified x-value and y-value animated. /// /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: @objc open func centerViewToAnimated( xValue: Double, yValue: Double, axis: YAxis.AxisDependency, duration: TimeInterval) { centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .easeInOutSine) } /// Sets custom offsets for the current `ChartViewPort` (the offsets on the sides of the actual chart window). Setting this will prevent the chart from automatically calculating it's offsets. Use `resetViewPortOffsets()` to undo this. /// ONLY USE THIS WHEN YOU KNOW WHAT YOU ARE DOING, else use `setExtraOffsets(...)`. @objc open func setViewPortOffsets(left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat) { _customViewPortEnabled = true if Thread.isMainThread { self._viewPortHandler.restrainViewPort(offsetLeft: left, offsetTop: top, offsetRight: right, offsetBottom: bottom) prepareOffsetMatrix() prepareValuePxMatrix() } else { DispatchQueue.main.async(execute: { self.setViewPortOffsets(left: left, top: top, right: right, bottom: bottom) }) } } /// Resets all custom offsets set via `setViewPortOffsets(...)` method. Allows the chart to again calculate all offsets automatically. @objc open func resetViewPortOffsets() { _customViewPortEnabled = false calculateOffsets() } // MARK: - Accessors /// - returns: The range of the specified axis. @objc open func getAxisRange(axis: YAxis.AxisDependency) -> Double { if axis == .left { return leftAxis.axisRange } else { return rightAxis.axisRange } } /// - returns: The position (in pixels) the provided Entry has inside the chart view @objc open func getPosition(entry e: ChartDataEntry, axis: YAxis.AxisDependency) -> CGPoint { var vals = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)) getTransformer(forAxis: axis).pointValueToPixel(&vals) return vals } /// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling). @objc open var dragEnabled: Bool { get { return _dragXEnabled || _dragYEnabled } set { _dragYEnabled = newValue _dragXEnabled = newValue } } /// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling). @objc open var isDragEnabled: Bool { return dragEnabled } /// is dragging on the X axis enabled? open var dragXEnabled: Bool { get { return _dragXEnabled } set { _dragXEnabled = newValue } } /// is dragging on the Y axis enabled? open var dragYEnabled: Bool { get { return _dragYEnabled } set { _dragYEnabled = newValue } } /// is scaling enabled? (zooming in and out by gesture) for the chart (this does not affect dragging). @objc open func setScaleEnabled(_ enabled: Bool) { if _scaleXEnabled != enabled || _scaleYEnabled != enabled { _scaleXEnabled = enabled _scaleYEnabled = enabled #if !os(tvOS) _pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled #endif } } @objc open var scaleXEnabled: Bool { get { return _scaleXEnabled } set { if _scaleXEnabled != newValue { _scaleXEnabled = newValue #if !os(tvOS) _pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled #endif } } } @objc open var scaleYEnabled: Bool { get { return _scaleYEnabled } set { if _scaleYEnabled != newValue { _scaleYEnabled = newValue #if !os(tvOS) _pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled #endif } } } @objc open var isScaleXEnabled: Bool { return scaleXEnabled } @objc open var isScaleYEnabled: Bool { return scaleYEnabled } /// flag that indicates if double tap zoom is enabled or not @objc open var doubleTapToZoomEnabled: Bool { get { return _doubleTapToZoomEnabled } set { if _doubleTapToZoomEnabled != newValue { _doubleTapToZoomEnabled = newValue _doubleTapGestureRecognizer.isEnabled = _doubleTapToZoomEnabled } } } /// **default**: true /// - returns: `true` if zooming via double-tap is enabled `false` ifnot. @objc open var isDoubleTapToZoomEnabled: Bool { return doubleTapToZoomEnabled } /// flag that indicates if highlighting per dragging over a fully zoomed out chart is enabled @objc open var highlightPerDragEnabled = true /// If set to true, highlighting per dragging over a fully zoomed out chart is enabled /// You might want to disable this when using inside a `NSUIScrollView` /// /// **default**: true @objc open var isHighlightPerDragEnabled: Bool { return highlightPerDragEnabled } /// **default**: true /// - returns: `true` if drawing the grid background is enabled, `false` ifnot. @objc open var isDrawGridBackgroundEnabled: Bool { return drawGridBackgroundEnabled } /// **default**: false /// - returns: `true` if drawing the borders rectangle is enabled, `false` ifnot. @objc open var isDrawBordersEnabled: Bool { return drawBordersEnabled } /// - returns: The x and y values in the chart at the given touch point /// (encapsulated in a `CGPoint`). This method transforms pixel coordinates to /// coordinates / values in the chart. This is the opposite method to /// `getPixelsForValues(...)`. @objc open func valueForTouchPoint(point pt: CGPoint, axis: YAxis.AxisDependency) -> CGPoint { return getTransformer(forAxis: axis).valueForTouchPoint(pt) } /// Transforms the given chart values into pixels. This is the opposite /// method to `valueForTouchPoint(...)`. @objc open func pixelForValues(x: Double, y: Double, axis: YAxis.AxisDependency) -> CGPoint { return getTransformer(forAxis: axis).pixelForValues(x: x, y: y) } /// - returns: The Entry object displayed at the touched position of the chart @objc open func getEntryByTouchPoint(point pt: CGPoint) -> ChartDataEntry! { if let h = getHighlightByTouchPoint(pt) { return _data!.entryForHighlight(h) } return nil } /// - returns: The DataSet object displayed at the touched position of the chart @objc open func getDataSetByTouchPoint(point pt: CGPoint) -> IBarLineScatterCandleBubbleChartDataSet? { let h = getHighlightByTouchPoint(pt) if h !== nil { return _data?.getDataSetByIndex(h!.dataSetIndex) as? IBarLineScatterCandleBubbleChartDataSet } return nil } /// - returns: The current x-scale factor @objc open var scaleX: CGFloat { if _viewPortHandler === nil { return 1.0 } return _viewPortHandler.scaleX } /// - returns: The current y-scale factor @objc open var scaleY: CGFloat { if _viewPortHandler === nil { return 1.0 } return _viewPortHandler.scaleY } /// if the chart is fully zoomed out, return true @objc open var isFullyZoomedOut: Bool { return _viewPortHandler.isFullyZoomedOut } /// - returns: The y-axis object to the corresponding AxisDependency. In the /// horizontal bar-chart, LEFT == top, RIGHT == BOTTOM @objc open func getAxis(_ axis: YAxis.AxisDependency) -> YAxis { if axis == .left { return leftAxis } else { return rightAxis } } /// flag that indicates if pinch-zoom is enabled. if true, both x and y axis can be scaled simultaneously with 2 fingers, if false, x and y axis can be scaled separately @objc open var pinchZoomEnabled: Bool { get { return _pinchZoomEnabled } set { if _pinchZoomEnabled != newValue { _pinchZoomEnabled = newValue #if !os(tvOS) _pinchGestureRecognizer.isEnabled = _pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled #endif } } } /// **default**: false /// - returns: `true` if pinch-zoom is enabled, `false` ifnot @objc open var isPinchZoomEnabled: Bool { return pinchZoomEnabled } /// Set an offset in dp that allows the user to drag the chart over it's /// bounds on the x-axis. @objc open func setDragOffsetX(_ offset: CGFloat) { _viewPortHandler.setDragOffsetX(offset) } /// Set an offset in dp that allows the user to drag the chart over it's /// bounds on the y-axis. @objc open func setDragOffsetY(_ offset: CGFloat) { _viewPortHandler.setDragOffsetY(offset) } /// - returns: `true` if both drag offsets (x and y) are zero or smaller. @objc open var hasNoDragOffset: Bool { return _viewPortHandler.hasNoDragOffset } open override var chartYMax: Double { return max(leftAxis._axisMaximum, rightAxis._axisMaximum) } open override var chartYMin: Double { return min(leftAxis._axisMinimum, rightAxis._axisMinimum) } /// - returns: `true` if either the left or the right or both axes are inverted. @objc open var isAnyAxisInverted: Bool { return leftAxis.isInverted || rightAxis.isInverted } /// flag that indicates if auto scaling on the y axis is enabled. /// if yes, the y axis automatically adjusts to the min and max y values of the current x axis range whenever the viewport changes @objc open var autoScaleMinMaxEnabled: Bool { get { return _autoScaleMinMaxEnabled } set { _autoScaleMinMaxEnabled = newValue } } /// **default**: false /// - returns: `true` if auto scaling on the y axis is enabled. @objc open var isAutoScaleMinMaxEnabled : Bool { return autoScaleMinMaxEnabled } /// Sets a minimum width to the specified y axis. @objc open func setYAxisMinWidth(_ axis: YAxis.AxisDependency, width: CGFloat) { if axis == .left { leftAxis.minWidth = width } else { rightAxis.minWidth = width } } /// **default**: 0.0 /// - returns: The (custom) minimum width of the specified Y axis. @objc open func getYAxisMinWidth(_ axis: YAxis.AxisDependency) -> CGFloat { if axis == .left { return leftAxis.minWidth } else { return rightAxis.minWidth } } /// Sets a maximum width to the specified y axis. /// Zero (0.0) means there's no maximum width @objc open func setYAxisMaxWidth(_ axis: YAxis.AxisDependency, width: CGFloat) { if axis == .left { leftAxis.maxWidth = width } else { rightAxis.maxWidth = width } } /// Zero (0.0) means there's no maximum width /// /// **default**: 0.0 (no maximum specified) /// - returns: The (custom) maximum width of the specified Y axis. @objc open func getYAxisMaxWidth(_ axis: YAxis.AxisDependency) -> CGFloat { if axis == .left { return leftAxis.maxWidth } else { return rightAxis.maxWidth } } /// - returns the width of the specified y axis. @objc open func getYAxisWidth(_ axis: YAxis.AxisDependency) -> CGFloat { if axis == .left { return leftAxis.requiredSize().width } else { return rightAxis.requiredSize().width } } // MARK: - BarLineScatterCandleBubbleChartDataProvider /// - returns: The Transformer class that contains all matrices and is /// responsible for transforming values into pixels on the screen and /// backwards. open func getTransformer(forAxis axis: YAxis.AxisDependency) -> Transformer { if axis == .left { return _leftAxisTransformer } else { return _rightAxisTransformer } } /// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled open override var maxVisibleCount: Int { get { return _maxVisibleCount } set { _maxVisibleCount = newValue } } open func isInverted(axis: YAxis.AxisDependency) -> Bool { return getAxis(axis).isInverted } /// - returns: The lowest x-index (value on the x-axis) that is still visible on he chart. open var lowestVisibleX: Double { var pt = CGPoint( x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom) getTransformer(forAxis: .left).pixelToValues(&pt) return max(xAxis._axisMinimum, Double(pt.x)) } /// - returns: The highest x-index (value on the x-axis) that is still visible on the chart. open var highestVisibleX: Double { var pt = CGPoint( x: viewPortHandler.contentRight, y: viewPortHandler.contentBottom) getTransformer(forAxis: .left).pixelToValues(&pt) return min(xAxis._axisMaximum, Double(pt.x)) } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/BubbleChartView.swift ================================================ // // BubbleChartView.swift // Charts // // Bubble chart implementation: // Copyright 2015 Pierre-Marc Airoldi // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BubbleChartView: BarLineChartViewBase, BubbleChartDataProvider { open override func initialize() { super.initialize() renderer = BubbleChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) } // MARK: - BubbleChartDataProvider open var bubbleData: BubbleChartData? { return _data as? BubbleChartData } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/CandleStickChartView.swift ================================================ // // CandleStickChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Financial chart type that draws candle-sticks. open class CandleStickChartView: BarLineChartViewBase, CandleChartDataProvider { internal override func initialize() { super.initialize() renderer = CandleStickChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) self.xAxis.spaceMin = 0.5 self.xAxis.spaceMax = 0.5 } // MARK: - CandleChartDataProvider open var candleData: CandleChartData? { return _data as? CandleChartData } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/ChartViewBase.swift ================================================ // // ChartViewBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // // Based on https://github.com/PhilJay/MPAndroidChart/commit/c42b880 import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc public protocol ChartViewDelegate { /// Called when a value has been selected inside the chart. /// - parameter entry: The selected Entry. /// - parameter highlight: The corresponding highlight object that contains information about the highlighted position such as dataSetIndex etc. @objc optional func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) // Called when nothing has been selected or an "un-select" has been made. @objc optional func chartValueNothingSelected(_ chartView: ChartViewBase) // Callbacks when the chart is scaled / zoomed via pinch zoom gesture. @objc optional func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) // Callbacks when the chart is moved / translated via drag gesture. @objc optional func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) } open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate { // MARK: - Properties /// - returns: The object representing all x-labels, this method can be used to /// acquire the XAxis object and modify it (e.g. change the position of the /// labels) @objc open var xAxis: XAxis { return _xAxis } /// The default IValueFormatter that has been determined by the chart considering the provided minimum and maximum values. internal var _defaultValueFormatter: IValueFormatter? = DefaultValueFormatter(decimals: 0) /// object that holds all data that was originally set for the chart, before it was modified or any filtering algorithms had been applied internal var _data: ChartData? /// Flag that indicates if highlighting per tap (touch) is enabled private var _highlightPerTapEnabled = true /// If set to true, chart continues to scroll after touch up @objc open var dragDecelerationEnabled = true /// Deceleration friction coefficient in [0 ; 1] interval, higher values indicate that speed will decrease slowly, for example if it set to 0, it will stop immediately. /// 1 is an invalid value, and will be converted to 0.999 automatically. private var _dragDecelerationFrictionCoef: CGFloat = 0.9 /// if true, units are drawn next to the values in the chart internal var _drawUnitInChart = false /// The object representing the labels on the x-axis internal var _xAxis: XAxis! /// The `Description` object of the chart. /// This should have been called just "description", but @objc open var chartDescription: Description? /// The legend object containing all data associated with the legend internal var _legend: Legend! /// delegate to receive chart events @objc open weak var delegate: ChartViewDelegate? /// text that is displayed when the chart is empty @objc open var noDataText = "No chart data available." /// Font to be used for the no data text. @objc open var noDataFont: NSUIFont! = NSUIFont(name: "HelveticaNeue", size: 12.0) /// color of the no data text @objc open var noDataTextColor: NSUIColor = NSUIColor.black /// alignment of the no data text open var noDataTextAlignment: NSTextAlignment = .left internal var _legendRenderer: LegendRenderer! /// object responsible for rendering the data @objc open var renderer: DataRenderer? @objc open var highlighter: IHighlighter? /// object that manages the bounds and drawing constraints of the chart internal var _viewPortHandler: ViewPortHandler! /// object responsible for animations internal var _animator: Animator! /// flag that indicates if offsets calculation has already been done or not private var _offsetsCalculated = false /// array of Highlight objects that reference the highlighted slices in the chart internal var _indicesToHighlight = [Highlight]() /// `true` if drawing the marker is enabled when tapping on values /// (use the `marker` property to specify a marker) @objc open var drawMarkers = true /// - returns: `true` if drawing the marker is enabled when tapping on values /// (use the `marker` property to specify a marker) @objc open var isDrawMarkersEnabled: Bool { return drawMarkers } /// The marker that is displayed when a value is clicked on the chart @objc open var marker: IMarker? private var _interceptTouchEvents = false /// An extra offset to be appended to the viewport's top @objc open var extraTopOffset: CGFloat = 0.0 /// An extra offset to be appended to the viewport's right @objc open var extraRightOffset: CGFloat = 0.0 /// An extra offset to be appended to the viewport's bottom @objc open var extraBottomOffset: CGFloat = 0.0 /// An extra offset to be appended to the viewport's left @objc open var extraLeftOffset: CGFloat = 0.0 @objc open func setExtraOffsets(left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat) { extraLeftOffset = left extraTopOffset = top extraRightOffset = right extraBottomOffset = bottom } // MARK: - Initializers public override init(frame: CGRect) { super.init(frame: frame) initialize() } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) initialize() } deinit { self.removeObserver(self, forKeyPath: "bounds") self.removeObserver(self, forKeyPath: "frame") } internal func initialize() { #if os(iOS) self.backgroundColor = NSUIColor.clear #endif _animator = Animator() _animator.delegate = self _viewPortHandler = ViewPortHandler(width: bounds.size.width, height: bounds.size.height) chartDescription = Description() _legend = Legend() _legendRenderer = LegendRenderer(viewPortHandler: _viewPortHandler, legend: _legend) _xAxis = XAxis() self.addObserver(self, forKeyPath: "bounds", options: .new, context: nil) self.addObserver(self, forKeyPath: "frame", options: .new, context: nil) } // MARK: - ChartViewBase /// The data for the chart open var data: ChartData? { get { return _data } set { _data = newValue _offsetsCalculated = false guard let _data = _data else { setNeedsDisplay() return } // calculate how many digits are needed setupDefaultFormatter(min: _data.getYMin(), max: _data.getYMax()) for set in _data.dataSets { if set.needsFormatter || set.valueFormatter === _defaultValueFormatter { set.valueFormatter = _defaultValueFormatter } } // let the chart know there is new data notifyDataSetChanged() } } /// Clears the chart from all data (sets it to null) and refreshes it (by calling setNeedsDisplay()). @objc open func clear() { _data = nil _offsetsCalculated = false _indicesToHighlight.removeAll() lastHighlighted = nil setNeedsDisplay() } /// Removes all DataSets (and thereby Entries) from the chart. Does not set the data object to nil. Also refreshes the chart by calling setNeedsDisplay(). @objc open func clearValues() { _data?.clearValues() setNeedsDisplay() } /// - returns: `true` if the chart is empty (meaning it's data object is either null or contains no entries). @objc open func isEmpty() -> Bool { guard let data = _data else { return true } if data.entryCount <= 0 { return true } else { return false } } /// Lets the chart know its underlying data has changed and should perform all necessary recalculations. /// It is crucial that this method is called everytime data is changed dynamically. Not calling this method can lead to crashes or unexpected behaviour. @objc open func notifyDataSetChanged() { fatalError("notifyDataSetChanged() cannot be called on ChartViewBase") } /// Calculates the offsets of the chart to the border depending on the position of an eventual legend or depending on the length of the y-axis and x-axis labels and their position internal func calculateOffsets() { fatalError("calculateOffsets() cannot be called on ChartViewBase") } /// calcualtes the y-min and y-max value and the y-delta and x-delta value internal func calcMinMax() { fatalError("calcMinMax() cannot be called on ChartViewBase") } /// calculates the required number of digits for the values that might be drawn in the chart (if enabled), and creates the default value formatter internal func setupDefaultFormatter(min: Double, max: Double) { // check if a custom formatter is set or not var reference = Double(0.0) if let data = _data , data.entryCount >= 2 { reference = fabs(max - min) } else { let absMin = fabs(min) let absMax = fabs(max) reference = absMin > absMax ? absMin : absMax } if _defaultValueFormatter is DefaultValueFormatter { // setup the formatter with a new number of digits let digits = reference.decimalPlaces (_defaultValueFormatter as? DefaultValueFormatter)?.decimals = digits } } open override func draw(_ rect: CGRect) { let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext else { return } let frame = self.bounds if _data === nil && noDataText.count > 0 { context.saveGState() defer { context.restoreGState() } let paragraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.minimumLineHeight = noDataFont.lineHeight paragraphStyle.lineBreakMode = .byWordWrapping paragraphStyle.alignment = noDataTextAlignment ChartUtils.drawMultilineText( context: context, text: noDataText, point: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0), attributes: [.font: noDataFont, .foregroundColor: noDataTextColor, .paragraphStyle: paragraphStyle], constrainedToSize: self.bounds.size, anchor: CGPoint(x: 0.5, y: 0.5), angleRadians: 0.0) return } if !_offsetsCalculated { calculateOffsets() _offsetsCalculated = true } } /// Draws the description text in the bottom right corner of the chart (per default) internal func drawDescription(context: CGContext) { // check if description should be drawn guard let description = chartDescription, description.isEnabled, let descriptionText = description.text, descriptionText.count > 0 else { return } let position = description.position ?? CGPoint(x: bounds.width - _viewPortHandler.offsetRight - description.xOffset, y: bounds.height - _viewPortHandler.offsetBottom - description.yOffset - description.font.lineHeight) var attrs = [NSAttributedString.Key : Any]() attrs[NSAttributedString.Key.font] = description.font attrs[NSAttributedString.Key.foregroundColor] = description.textColor ChartUtils.drawText( context: context, text: descriptionText, point: position, align: description.textAlign, attributes: attrs) } // MARK: - Highlighting /// - returns: The array of currently highlighted values. This might an empty if nothing is highlighted. @objc open var highlighted: [Highlight] { return _indicesToHighlight } /// Set this to false to prevent values from being highlighted by tap gesture. /// Values can still be highlighted via drag or programmatically. /// **default**: true @objc open var highlightPerTapEnabled: Bool { get { return _highlightPerTapEnabled } set { _highlightPerTapEnabled = newValue } } /// - returns: `true` if values can be highlighted via tap gesture, `false` ifnot. @objc open var isHighLightPerTapEnabled: Bool { return highlightPerTapEnabled } /// Checks if the highlight array is null, has a length of zero or if the first object is null. /// - returns: `true` if there are values to highlight, `false` ifthere are no values to highlight. @objc open func valuesToHighlight() -> Bool { return _indicesToHighlight.count > 0 } /// Highlights the values at the given indices in the given DataSets. Provide /// null or an empty array to undo all highlighting. /// This should be used to programmatically highlight values. /// This method *will not* call the delegate. @objc open func highlightValues(_ highs: [Highlight]?) { // set the indices to highlight _indicesToHighlight = highs ?? [Highlight]() if _indicesToHighlight.isEmpty { self.lastHighlighted = nil } else { self.lastHighlighted = _indicesToHighlight[0] } // redraw the chart setNeedsDisplay() } /// Highlights any y-value at the given x-value in the given DataSet. /// Provide -1 as the dataSetIndex to undo all highlighting. /// This method will call the delegate. /// - parameter x: The x-value to highlight /// - parameter dataSetIndex: The dataset index to search in /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) @objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1) { highlightValue(x: x, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true) } /// Highlights the value at the given x-value and y-value in the given DataSet. /// Provide -1 as the dataSetIndex to undo all highlighting. /// This method will call the delegate. /// - parameter x: The x-value to highlight /// - parameter y: The y-value to highlight. Supply `NaN` for "any" /// - parameter dataSetIndex: The dataset index to search in /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1) { highlightValue(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: true) } /// Highlights any y-value at the given x-value in the given DataSet. /// Provide -1 as the dataSetIndex to undo all highlighting. /// - parameter x: The x-value to highlight /// - parameter dataSetIndex: The dataset index to search in /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) /// - parameter callDelegate: Should the delegate be called for this change @objc open func highlightValue(x: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool) { highlightValue(x: x, y: .nan, dataSetIndex: dataSetIndex, dataIndex: dataIndex, callDelegate: callDelegate) } /// Highlights the value at the given x-value and y-value in the given DataSet. /// Provide -1 as the dataSetIndex to undo all highlighting. /// - parameter x: The x-value to highlight /// - parameter y: The y-value to highlight. Supply `NaN` for "any" /// - parameter dataSetIndex: The dataset index to search in /// - parameter dataIndex: The data index to search in (only used in CombinedChartView currently) /// - parameter callDelegate: Should the delegate be called for this change @objc open func highlightValue(x: Double, y: Double, dataSetIndex: Int, dataIndex: Int = -1, callDelegate: Bool) { guard let data = _data else { Swift.print("Value not highlighted because data is nil") return } if dataSetIndex < 0 || dataSetIndex >= data.dataSetCount { highlightValue(nil, callDelegate: callDelegate) } else { highlightValue(Highlight(x: x, y: y, dataSetIndex: dataSetIndex, dataIndex: dataIndex), callDelegate: callDelegate) } } /// Highlights the values represented by the provided Highlight object /// This method *will not* call the delegate. /// - parameter highlight: contains information about which entry should be highlighted @objc open func highlightValue(_ highlight: Highlight?) { highlightValue(highlight, callDelegate: false) } /// Highlights the value selected by touch gesture. @objc open func highlightValue(_ highlight: Highlight?, callDelegate: Bool) { var entry: ChartDataEntry? var h = highlight if h == nil { self.lastHighlighted = nil _indicesToHighlight.removeAll(keepingCapacity: false) } else { // set the indices to highlight entry = _data?.entryForHighlight(h!) if entry == nil { h = nil _indicesToHighlight.removeAll(keepingCapacity: false) } else { _indicesToHighlight = [h!] } } if callDelegate, let delegate = delegate { if let h = h { // notify the listener delegate.chartValueSelected?(self, entry: entry!, highlight: h) } else { delegate.chartValueNothingSelected?(self) } } // redraw the chart setNeedsDisplay() } /// - returns: The Highlight object (contains x-index and DataSet index) of the /// selected value at the given touch point inside the Line-, Scatter-, or /// CandleStick-Chart. @objc open func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight? { if _data === nil { Swift.print("Can't select by touch. No data set.") return nil } return self.highlighter?.getHighlight(x: pt.x, y: pt.y) } /// The last value that was highlighted via touch. @objc open var lastHighlighted: Highlight? // MARK: - Markers /// draws all MarkerViews on the highlighted positions internal func drawMarkers(context: CGContext) { // if there is no marker view or drawing marker is disabled guard let marker = marker , isDrawMarkersEnabled && valuesToHighlight() else { return } for i in 0 ..< _indicesToHighlight.count { let highlight = _indicesToHighlight[i] guard let set = data?.getDataSetByIndex(highlight.dataSetIndex), let e = _data?.entryForHighlight(highlight) else { continue } let entryIndex = set.entryIndex(entry: e) if entryIndex > Int(Double(set.entryCount) * _animator.phaseX) { continue } let pos = getMarkerPosition(highlight: highlight) // check bounds if !_viewPortHandler.isInBounds(x: pos.x, y: pos.y) { continue } // callbacks to update the content marker.refreshContent(entry: e, highlight: highlight) // draw the marker marker.draw(context: context, point: pos) } } /// - returns: The actual position in pixels of the MarkerView for the given Entry in the given DataSet. @objc open func getMarkerPosition(highlight: Highlight) -> CGPoint { return CGPoint(x: highlight.drawX, y: highlight.drawY) } // MARK: - Animation /// - returns: The animator responsible for animating chart values. @objc open var chartAnimator: Animator! { return _animator } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingX: an easing function for the animation on the x axis /// - parameter easingY: an easing function for the animation on the y axis @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingX: ChartEasingFunctionBlock?, easingY: ChartEasingFunctionBlock?) { _animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingX: easingX, easingY: easingY) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOptionX: the easing function for the animation on the x axis /// - parameter easingOptionY: the easing function for the animation on the y axis @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOptionX: ChartEasingOption, easingOptionY: ChartEasingOption) { _animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingOptionX: easingOptionX, easingOptionY: easingOptionY) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easing: an easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { _animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easing: easing) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOption: the easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval, easingOption: ChartEasingOption) { _animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration, easingOption: easingOption) } /// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter yAxisDuration: duration for animating the y axis @objc open func animate(xAxisDuration: TimeInterval, yAxisDuration: TimeInterval) { _animator.animate(xAxisDuration: xAxisDuration, yAxisDuration: yAxisDuration) } /// Animates the drawing / rendering of the chart the x-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter easing: an easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { _animator.animate(xAxisDuration: xAxisDuration, easing: easing) } /// Animates the drawing / rendering of the chart the x-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis /// - parameter easingOption: the easing function for the animation @objc open func animate(xAxisDuration: TimeInterval, easingOption: ChartEasingOption) { _animator.animate(xAxisDuration: xAxisDuration, easingOption: easingOption) } /// Animates the drawing / rendering of the chart the x-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter xAxisDuration: duration for animating the x axis @objc open func animate(xAxisDuration: TimeInterval) { _animator.animate(xAxisDuration: xAxisDuration) } /// Animates the drawing / rendering of the chart the y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easing: an easing function for the animation @objc open func animate(yAxisDuration: TimeInterval, easing: ChartEasingFunctionBlock?) { _animator.animate(yAxisDuration: yAxisDuration, easing: easing) } /// Animates the drawing / rendering of the chart the y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter yAxisDuration: duration for animating the y axis /// - parameter easingOption: the easing function for the animation @objc open func animate(yAxisDuration: TimeInterval, easingOption: ChartEasingOption) { _animator.animate(yAxisDuration: yAxisDuration, easingOption: easingOption) } /// Animates the drawing / rendering of the chart the y-axis with the specified animation time. /// If `animate(...)` is called, no further calling of `invalidate()` is necessary to refresh the chart. /// - parameter yAxisDuration: duration for animating the y axis @objc open func animate(yAxisDuration: TimeInterval) { _animator.animate(yAxisDuration: yAxisDuration) } // MARK: - Accessors /// - returns: The current y-max value across all DataSets open var chartYMax: Double { return _data?.yMax ?? 0.0 } /// - returns: The current y-min value across all DataSets open var chartYMin: Double { return _data?.yMin ?? 0.0 } open var chartXMax: Double { return _xAxis._axisMaximum } open var chartXMin: Double { return _xAxis._axisMinimum } open var xRange: Double { return _xAxis.axisRange } /// * /// - note: (Equivalent of getCenter() in MPAndroidChart, as center is already a standard in iOS that returns the center point relative to superview, and MPAndroidChart returns relative to self)* /// - returns: The center point of the chart (the whole View) in pixels. @objc open var midPoint: CGPoint { let bounds = self.bounds return CGPoint(x: bounds.origin.x + bounds.size.width / 2.0, y: bounds.origin.y + bounds.size.height / 2.0) } /// - returns: The center of the chart taking offsets under consideration. (returns the center of the content rectangle) open var centerOffsets: CGPoint { return _viewPortHandler.contentCenter } /// - returns: The Legend object of the chart. This method can be used to get an instance of the legend in order to customize the automatically generated Legend. @objc open var legend: Legend { return _legend } /// - returns: The renderer object responsible for rendering / drawing the Legend. @objc open var legendRenderer: LegendRenderer! { return _legendRenderer } /// - returns: The rectangle that defines the borders of the chart-value surface (into which the actual values are drawn). @objc open var contentRect: CGRect { return _viewPortHandler.contentRect } /// - returns: The ViewPortHandler of the chart that is responsible for the /// content area of the chart and its offsets and dimensions. @objc open var viewPortHandler: ViewPortHandler! { return _viewPortHandler } /// - returns: The bitmap that represents the chart. @objc open func getChartImage(transparent: Bool) -> NSUIImage? { NSUIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque || !transparent, NSUIMainScreen()?.nsuiScale ?? 1.0) guard let context = NSUIGraphicsGetCurrentContext() else { return nil } let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size) if isOpaque || !transparent { // Background color may be partially transparent, we must fill with white if we want to output an opaque image context.setFillColor(NSUIColor.white.cgColor) context.fill(rect) if let backgroundColor = self.backgroundColor { context.setFillColor(backgroundColor.cgColor) context.fill(rect) } } nsuiLayer?.render(in: context) let image = NSUIGraphicsGetImageFromCurrentImageContext() NSUIGraphicsEndImageContext() return image } public enum ImageFormat { case jpeg case png } /// Saves the current chart state with the given name to the given path on /// the sdcard leaving the path empty "" will put the saved file directly on /// the SD card chart is saved as a PNG image, example: /// saveToPath("myfilename", "foldername1/foldername2") /// /// - parameter to: path to the image to save /// - parameter format: the format to save /// - parameter compressionQuality: compression quality for lossless formats (JPEG) /// /// - returns: `true` if the image was saved successfully open func save(to path: String, format: ImageFormat, compressionQuality: Double) -> Bool { guard let image = getChartImage(transparent: format != .jpeg) else { return false } let imageData: Data? switch (format) { case .png: imageData = NSUIImagePNGRepresentation(image) case .jpeg: imageData = NSUIImageJPEGRepresentation(image, CGFloat(compressionQuality)) } guard let data = imageData else { return false } do { try data.write(to: URL(fileURLWithPath: path), options: .atomic) } catch { return false } return true } internal var _viewportJobs = [ViewPortJob]() open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "bounds" || keyPath == "frame" { let bounds = self.bounds if (_viewPortHandler !== nil && (bounds.size.width != _viewPortHandler.chartWidth || bounds.size.height != _viewPortHandler.chartHeight)) { _viewPortHandler.setChartDimens(width: bounds.size.width, height: bounds.size.height) // This may cause the chart view to mutate properties affecting the view port -- lets do this // before we try to run any pending jobs on the view port itself notifyDataSetChanged() // Finish any pending viewport changes while (!_viewportJobs.isEmpty) { let job = _viewportJobs.remove(at: 0) job.doJob() } } } } @objc open func removeViewportJob(_ job: ViewPortJob) { if let index = _viewportJobs.index(where: { $0 === job }) { _viewportJobs.remove(at: index) } } @objc open func clearAllViewportJobs() { _viewportJobs.removeAll(keepingCapacity: false) } @objc open func addViewportJob(_ job: ViewPortJob) { if _viewPortHandler.hasChartDimens { job.doJob() } else { _viewportJobs.append(job) } } /// **default**: true /// - returns: `true` if chart continues to scroll after touch up, `false` ifnot. @objc open var isDragDecelerationEnabled: Bool { return dragDecelerationEnabled } /// Deceleration friction coefficient in [0 ; 1] interval, higher values indicate that speed will decrease slowly, for example if it set to 0, it will stop immediately. /// 1 is an invalid value, and will be converted to 0.999 automatically. /// /// **default**: true @objc open var dragDecelerationFrictionCoef: CGFloat { get { return _dragDecelerationFrictionCoef } set { var val = newValue if val < 0.0 { val = 0.0 } if val >= 1.0 { val = 0.999 } _dragDecelerationFrictionCoef = val } } /// The maximum distance in screen pixels away from an entry causing it to highlight. /// **default**: 500.0 open var maxHighlightDistance: CGFloat = 500.0 /// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled open var maxVisibleCount: Int { return Int(INT_MAX) } // MARK: - AnimatorDelegate open func animatorUpdated(_ chartAnimator: Animator) { setNeedsDisplay() } open func animatorStopped(_ chartAnimator: Animator) { } // MARK: - Touches open override func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) { if !_interceptTouchEvents { super.nsuiTouchesBegan(touches, withEvent: event) } } open override func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) { if !_interceptTouchEvents { super.nsuiTouchesMoved(touches, withEvent: event) } } open override func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) { if !_interceptTouchEvents { super.nsuiTouchesEnded(touches, withEvent: event) } } open override func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) { if !_interceptTouchEvents { super.nsuiTouchesCancelled(touches, withEvent: event) } } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/CombinedChartView.swift ================================================ // // CombinedChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// This chart class allows the combination of lines, bars, scatter and candle data all displayed in one chart area. open class CombinedChartView: BarLineChartViewBase, CombinedChartDataProvider { /// the fill-formatter used for determining the position of the fill-line internal var _fillFormatter: IFillFormatter! /// enum that allows to specify the order in which the different data objects for the combined-chart are drawn @objc(CombinedChartDrawOrder) public enum DrawOrder: Int { case bar case bubble case line case candle case scatter } open override func initialize() { super.initialize() self.highlighter = CombinedHighlighter(chart: self, barDataProvider: self) // Old default behaviour self.highlightFullBarEnabled = true _fillFormatter = DefaultFillFormatter() renderer = CombinedChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler) } open override var data: ChartData? { get { return super.data } set { super.data = newValue self.highlighter = CombinedHighlighter(chart: self, barDataProvider: self) (renderer as? CombinedChartRenderer)?.createRenderers() renderer?.initBuffers() } } @objc open var fillFormatter: IFillFormatter { get { return _fillFormatter } set { _fillFormatter = newValue if _fillFormatter == nil { _fillFormatter = DefaultFillFormatter() } } } /// - returns: The Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the CombinedChart. open override func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight? { if _data === nil { Swift.print("Can't select by touch. No data set.") return nil } guard let h = self.highlighter?.getHighlight(x: pt.x, y: pt.y) else { return nil } if !isHighlightFullBarEnabled { return h } // For isHighlightFullBarEnabled, remove stackIndex return Highlight( x: h.x, y: h.y, xPx: h.xPx, yPx: h.yPx, dataIndex: h.dataIndex, dataSetIndex: h.dataSetIndex, stackIndex: -1, axis: h.axis) } // MARK: - CombinedChartDataProvider open var combinedData: CombinedChartData? { get { return _data as? CombinedChartData } } // MARK: - LineChartDataProvider open var lineData: LineChartData? { get { return combinedData?.lineData } } // MARK: - BarChartDataProvider open var barData: BarChartData? { get { return combinedData?.barData } } // MARK: - ScatterChartDataProvider open var scatterData: ScatterChartData? { get { return combinedData?.scatterData } } // MARK: - CandleChartDataProvider open var candleData: CandleChartData? { get { return combinedData?.candleData } } // MARK: - BubbleChartDataProvider open var bubbleData: BubbleChartData? { get { return combinedData?.bubbleData } } // MARK: - Accessors /// if set to true, all values are drawn above their bars, instead of below their top @objc open var drawValueAboveBarEnabled: Bool { get { return (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled } set { (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled = newValue } } /// if set to true, a grey area is drawn behind each bar that indicates the maximum value @objc open var drawBarShadowEnabled: Bool { get { return (renderer as! CombinedChartRenderer).drawBarShadowEnabled } set { (renderer as! CombinedChartRenderer).drawBarShadowEnabled = newValue } } /// - returns: `true` if drawing values above bars is enabled, `false` ifnot open var isDrawValueAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer).drawValueAboveBarEnabled } /// - returns: `true` if drawing shadows (maxvalue) for each bar is enabled, `false` ifnot open var isDrawBarShadowEnabled: Bool { return (renderer as! CombinedChartRenderer).drawBarShadowEnabled } /// the order in which the provided data objects should be drawn. /// The earlier you place them in the provided array, the further they will be in the background. /// e.g. if you provide [DrawOrder.Bar, DrawOrder.Line], the bars will be drawn behind the lines. @objc open var drawOrder: [Int] { get { return (renderer as! CombinedChartRenderer).drawOrder.map { $0.rawValue } } set { (renderer as! CombinedChartRenderer).drawOrder = newValue.map { DrawOrder(rawValue: $0)! } } } /// Set this to `true` to make the highlight operation full-bar oriented, `false` to make it highlight single values @objc open var highlightFullBarEnabled: Bool = false /// - returns: `true` the highlight is be full-bar oriented, `false` ifsingle-value open var isHighlightFullBarEnabled: Bool { return highlightFullBarEnabled } // MARK: - ChartViewBase /// draws all MarkerViews on the highlighted positions override func drawMarkers(context: CGContext) { guard let marker = marker, isDrawMarkersEnabled && valuesToHighlight() else { return } for i in 0 ..< _indicesToHighlight.count { let highlight = _indicesToHighlight[i] guard let set = combinedData?.getDataSetByHighlight(highlight), let e = _data?.entryForHighlight(highlight) else { continue } let entryIndex = set.entryIndex(entry: e) if entryIndex > Int(Double(set.entryCount) * _animator.phaseX) { continue } let pos = getMarkerPosition(highlight: highlight) // check bounds if !_viewPortHandler.isInBounds(x: pos.x, y: pos.y) { continue } // callbacks to update the content marker.refreshContent(entry: e, highlight: highlight) // draw the marker marker.draw(context: context, point: pos) } } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/HorizontalBarChartView.swift ================================================ // // HorizontalBarChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched. open class HorizontalBarChartView: BarChartView { internal override func initialize() { super.initialize() _leftAxisTransformer = TransformerHorizontalBarChart(viewPortHandler: _viewPortHandler) _rightAxisTransformer = TransformerHorizontalBarChart(viewPortHandler: _viewPortHandler) renderer = HorizontalBarChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) leftYAxisRenderer = YAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: leftAxis, transformer: _leftAxisTransformer) rightYAxisRenderer = YAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: rightAxis, transformer: _rightAxisTransformer) xAxisRenderer = XAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer, chart: self) self.highlighter = HorizontalBarHighlighter(chart: self) } internal override func calculateLegendOffsets(offsetLeft: inout CGFloat, offsetTop: inout CGFloat, offsetRight: inout CGFloat, offsetBottom: inout CGFloat) { guard let legend = _legend, legend.isEnabled, legend.drawInside else { return } // setup offsets for legend switch legend.orientation { case .vertical: switch legend.horizontalAlignment { case .left: offsetLeft += min(legend.neededWidth, _viewPortHandler.chartWidth * legend.maxSizePercent) + legend.xOffset case .right: offsetRight += min(legend.neededWidth, _viewPortHandler.chartWidth * legend.maxSizePercent) + legend.xOffset case .center: switch legend.verticalAlignment { case .top: offsetTop += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset case .bottom: offsetBottom += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset default: break } } case .horizontal: switch legend.verticalAlignment { case .top: offsetTop += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset // left axis equals the top x-axis in a horizontal chart if leftAxis.isEnabled && leftAxis.isDrawLabelsEnabled { offsetTop += leftAxis.getRequiredHeightSpace() } case .bottom: offsetBottom += min(legend.neededHeight, _viewPortHandler.chartHeight * legend.maxSizePercent) + legend.yOffset // right axis equals the bottom x-axis in a horizontal chart if rightAxis.isEnabled && rightAxis.isDrawLabelsEnabled { offsetBottom += rightAxis.getRequiredHeightSpace() } default: break } } } internal override func calculateOffsets() { var offsetLeft: CGFloat = 0.0, offsetRight: CGFloat = 0.0, offsetTop: CGFloat = 0.0, offsetBottom: CGFloat = 0.0 calculateLegendOffsets(offsetLeft: &offsetLeft, offsetTop: &offsetTop, offsetRight: &offsetRight, offsetBottom: &offsetBottom) // offsets for y-labels if leftAxis.needsOffset { offsetTop += leftAxis.getRequiredHeightSpace() } if rightAxis.needsOffset { offsetBottom += rightAxis.getRequiredHeightSpace() } let xlabelwidth = _xAxis.labelRotatedWidth if _xAxis.isEnabled { // offsets for x-labels if _xAxis.labelPosition == .bottom { offsetLeft += xlabelwidth } else if _xAxis.labelPosition == .top { offsetRight += xlabelwidth } else if _xAxis.labelPosition == .bothSided { offsetLeft += xlabelwidth offsetRight += xlabelwidth } } offsetTop += self.extraTopOffset offsetRight += self.extraRightOffset offsetBottom += self.extraBottomOffset offsetLeft += self.extraLeftOffset _viewPortHandler.restrainViewPort( offsetLeft: max(self.minOffset, offsetLeft), offsetTop: max(self.minOffset, offsetTop), offsetRight: max(self.minOffset, offsetRight), offsetBottom: max(self.minOffset, offsetBottom)) prepareOffsetMatrix() prepareValuePxMatrix() } internal override func prepareValuePxMatrix() { _rightAxisTransformer.prepareMatrixValuePx(chartXMin: rightAxis._axisMinimum, deltaX: CGFloat(rightAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum) _leftAxisTransformer.prepareMatrixValuePx(chartXMin: leftAxis._axisMinimum, deltaX: CGFloat(leftAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum) } open override func getMarkerPosition(highlight: Highlight) -> CGPoint { return CGPoint(x: highlight.drawY, y: highlight.drawX) } open override func getBarBounds(entry e: BarChartDataEntry) -> CGRect { guard let data = _data as? BarChartData, let set = data.getDataSetForEntry(e) as? IBarChartDataSet else { return CGRect.null } let y = e.y let x = e.x let barWidth = data.barWidth let top = x - 0.5 + barWidth / 2.0 let bottom = x + 0.5 - barWidth / 2.0 let left = y >= 0.0 ? y : 0.0 let right = y <= 0.0 ? y : 0.0 var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top) getTransformer(forAxis: set.axisDependency).rectValueToPixel(&bounds) return bounds } open override func getPosition(entry e: ChartDataEntry, axis: YAxis.AxisDependency) -> CGPoint { var vals = CGPoint(x: CGFloat(e.y), y: CGFloat(e.x)) getTransformer(forAxis: axis).pointValueToPixel(&vals) return vals } open override func getHighlightByTouchPoint(_ pt: CGPoint) -> Highlight? { if _data === nil { Swift.print("Can't select by touch. No data set.", terminator: "\n") return nil } return self.highlighter?.getHighlight(x: pt.y, y: pt.x) } /// - returns: The lowest x-index (value on the x-axis) that is still visible on he chart. open override var lowestVisibleX: Double { var pt = CGPoint( x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom) getTransformer(forAxis: .left).pixelToValues(&pt) return max(xAxis._axisMinimum, Double(pt.y)) } /// - returns: The highest x-index (value on the x-axis) that is still visible on the chart. open override var highestVisibleX: Double { var pt = CGPoint( x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop) getTransformer(forAxis: .left).pixelToValues(&pt) return min(xAxis._axisMaximum, Double(pt.y)) } // MARK: - Viewport open override func setVisibleXRangeMaximum(_ maxXRange: Double) { let xScale = xAxis.axisRange / maxXRange viewPortHandler.setMinimumScaleY(CGFloat(xScale)) } open override func setVisibleXRangeMinimum(_ minXRange: Double) { let xScale = xAxis.axisRange / minXRange viewPortHandler.setMaximumScaleY(CGFloat(xScale)) } open override func setVisibleXRange(minXRange: Double, maxXRange: Double) { let minScale = xAxis.axisRange / minXRange let maxScale = xAxis.axisRange / maxXRange viewPortHandler.setMinMaxScaleY(minScaleY: CGFloat(minScale), maxScaleY: CGFloat(maxScale)) } open override func setVisibleYRangeMaximum(_ maxYRange: Double, axis: YAxis.AxisDependency) { let yScale = getAxisRange(axis: axis) / maxYRange viewPortHandler.setMinimumScaleX(CGFloat(yScale)) } open override func setVisibleYRangeMinimum(_ minYRange: Double, axis: YAxis.AxisDependency) { let yScale = getAxisRange(axis: axis) / minYRange viewPortHandler.setMaximumScaleX(CGFloat(yScale)) } open override func setVisibleYRange(minYRange: Double, maxYRange: Double, axis: YAxis.AxisDependency) { let minScale = getAxisRange(axis: axis) / minYRange let maxScale = getAxisRange(axis: axis) / maxYRange viewPortHandler.setMinMaxScaleX(minScaleX: CGFloat(minScale), maxScaleX: CGFloat(maxScale)) } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/LineChartView.swift ================================================ // // LineChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Chart that draws lines, surfaces, circles, ... open class LineChartView: BarLineChartViewBase, LineChartDataProvider { internal override func initialize() { super.initialize() renderer = LineChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) } // MARK: - LineChartDataProvider open var lineData: LineChartData? { return _data as? LineChartData } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/PieChartView.swift ================================================ // // PieChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// View that represents a pie chart. Draws cake like slices. open class PieChartView: PieRadarChartViewBase { /// rect object that represents the bounds of the piechart, needed for drawing the circle private var _circleBox = CGRect() /// flag indicating if entry labels should be drawn or not private var _drawEntryLabelsEnabled = true /// array that holds the width of each pie-slice in degrees private var _drawAngles = [CGFloat]() /// array that holds the absolute angle in degrees of each slice private var _absoluteAngles = [CGFloat]() /// if true, the hole inside the chart will be drawn private var _drawHoleEnabled = true private var _holeColor: NSUIColor? = NSUIColor.white /// Sets the color the entry labels are drawn with. private var _entryLabelColor: NSUIColor? = NSUIColor.white /// Sets the font the entry labels are drawn with. private var _entryLabelFont: NSUIFont? = NSUIFont(name: "HelveticaNeue", size: 13.0) /// if true, the hole will see-through to the inner tips of the slices private var _drawSlicesUnderHoleEnabled = false /// if true, the values inside the piechart are drawn as percent values private var _usePercentValuesEnabled = false /// variable for the text that is drawn in the center of the pie-chart private var _centerAttributedText: NSAttributedString? /// the offset on the x- and y-axis the center text has in dp. private var _centerTextOffset: CGPoint = CGPoint() /// indicates the size of the hole in the center of the piechart /// /// **default**: `0.5` private var _holeRadiusPercent = CGFloat(0.5) private var _transparentCircleColor: NSUIColor? = NSUIColor(white: 1.0, alpha: 105.0/255.0) /// the radius of the transparent circle next to the chart-hole in the center private var _transparentCircleRadiusPercent = CGFloat(0.55) /// if enabled, centertext is drawn private var _drawCenterTextEnabled = true private var _centerTextRadiusPercent: CGFloat = 1.0 /// maximum angle for this pie private var _maxAngle: CGFloat = 360.0 public override init(frame: CGRect) { super.init(frame: frame) } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } internal override func initialize() { super.initialize() renderer = PieChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler) _xAxis = nil self.highlighter = PieHighlighter(chart: self) } open override func draw(_ rect: CGRect) { super.draw(rect) if _data === nil { return } let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext, let renderer = renderer else { return } renderer.drawData(context: context) if (valuesToHighlight()) { renderer.drawHighlighted(context: context, indices: _indicesToHighlight) } renderer.drawExtras(context: context) renderer.drawValues(context: context) legendRenderer.renderLegend(context: context) drawDescription(context: context) drawMarkers(context: context) } internal override func calculateOffsets() { super.calculateOffsets() // prevent nullpointer when no data set if _data === nil { return } let radius = diameter / 2.0 let c = self.centerOffsets let shift = (data as? PieChartData)?.dataSet?.selectionShift ?? 0.0 // create the circle box that will contain the pie-chart (the bounds of the pie-chart) _circleBox.origin.x = (c.x - radius) + shift _circleBox.origin.y = (c.y - radius) + shift _circleBox.size.width = diameter - shift * 2.0 _circleBox.size.height = diameter - shift * 2.0 } internal override func calcMinMax() { calcAngles() } open override func getMarkerPosition(highlight: Highlight) -> CGPoint { let center = self.centerCircleBox var r = self.radius var off = r / 10.0 * 3.6 if self.isDrawHoleEnabled { off = (r - (r * self.holeRadiusPercent)) / 2.0 } r -= off // offset to keep things inside the chart let rotationAngle = self.rotationAngle let entryIndex = Int(highlight.x) // offset needed to center the drawn text in the slice let offset = drawAngles[entryIndex] / 2.0 // calculate the text position let x: CGFloat = (r * cos(((rotationAngle + absoluteAngles[entryIndex] - offset) * CGFloat(_animator.phaseY)).DEG2RAD) + center.x) let y: CGFloat = (r * sin(((rotationAngle + absoluteAngles[entryIndex] - offset) * CGFloat(_animator.phaseY)).DEG2RAD) + center.y) return CGPoint(x: x, y: y) } /// calculates the needed angles for the chart slices private func calcAngles() { _drawAngles = [CGFloat]() _absoluteAngles = [CGFloat]() guard let data = _data else { return } let entryCount = data.entryCount _drawAngles.reserveCapacity(entryCount) _absoluteAngles.reserveCapacity(entryCount) let yValueSum = (_data as! PieChartData).yValueSum var dataSets = data.dataSets var cnt = 0 for i in 0 ..< data.dataSetCount { let set = dataSets[i] let entryCount = set.entryCount for j in 0 ..< entryCount { guard let e = set.entryForIndex(j) else { continue } _drawAngles.append(calcAngle(value: abs(e.y), yValueSum: yValueSum)) if cnt == 0 { _absoluteAngles.append(_drawAngles[cnt]) } else { _absoluteAngles.append(_absoluteAngles[cnt - 1] + _drawAngles[cnt]) } cnt += 1 } } } /// Checks if the given index is set to be highlighted. @objc open func needsHighlight(index: Int) -> Bool { // no highlight if !valuesToHighlight() { return false } for i in 0 ..< _indicesToHighlight.count { // check if the xvalue for the given dataset needs highlight if Int(_indicesToHighlight[i].x) == index { return true } } return false } /// calculates the needed angle for a given value private func calcAngle(_ value: Double) -> CGFloat { return calcAngle(value: value, yValueSum: (_data as! PieChartData).yValueSum) } /// calculates the needed angle for a given value private func calcAngle(value: Double, yValueSum: Double) -> CGFloat { return CGFloat(value) / CGFloat(yValueSum) * _maxAngle } /// This will throw an exception, PieChart has no XAxis object. open override var xAxis: XAxis { fatalError("PieChart has no XAxis") } open override func indexForAngle(_ angle: CGFloat) -> Int { // take the current angle of the chart into consideration let a = (angle - self.rotationAngle).normalizedAngle for i in 0 ..< _absoluteAngles.count { if _absoluteAngles[i] > a { return i } } return -1 // return -1 if no index found } /// - returns: The index of the DataSet this x-index belongs to. @objc open func dataSetIndexForIndex(_ xValue: Double) -> Int { var dataSets = _data?.dataSets ?? [] for i in 0 ..< dataSets.count { if (dataSets[i].entryForXValue(xValue, closestToY: Double.nan) !== nil) { return i } } return -1 } /// - returns: An integer array of all the different angles the chart slices /// have the angles in the returned array determine how much space (of 360°) /// each slice takes @objc open var drawAngles: [CGFloat] { return _drawAngles } /// - returns: The absolute angles of the different chart slices (where the /// slices end) @objc open var absoluteAngles: [CGFloat] { return _absoluteAngles } /// The color for the hole that is drawn in the center of the PieChart (if enabled). /// /// - note: Use holeTransparent with holeColor = nil to make the hole transparent.* @objc open var holeColor: NSUIColor? { get { return _holeColor } set { _holeColor = newValue setNeedsDisplay() } } /// if true, the hole will see-through to the inner tips of the slices /// /// **default**: `false` @objc open var drawSlicesUnderHoleEnabled: Bool { get { return _drawSlicesUnderHoleEnabled } set { _drawSlicesUnderHoleEnabled = newValue setNeedsDisplay() } } /// - returns: `true` if the inner tips of the slices are visible behind the hole, `false` if not. @objc open var isDrawSlicesUnderHoleEnabled: Bool { return drawSlicesUnderHoleEnabled } /// `true` if the hole in the center of the pie-chart is set to be visible, `false` ifnot @objc open var drawHoleEnabled: Bool { get { return _drawHoleEnabled } set { _drawHoleEnabled = newValue setNeedsDisplay() } } /// - returns: `true` if the hole in the center of the pie-chart is set to be visible, `false` ifnot @objc open var isDrawHoleEnabled: Bool { get { return drawHoleEnabled } } /// the text that is displayed in the center of the pie-chart @objc open var centerText: String? { get { return self.centerAttributedText?.string } set { var attrString: NSMutableAttributedString? if newValue == nil { attrString = nil } else { #if os(OSX) let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.lineBreakMode = NSParagraphStyle.LineBreakMode.byTruncatingTail #else let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.lineBreakMode = NSLineBreakMode.byTruncatingTail #endif paragraphStyle.alignment = .center attrString = NSMutableAttributedString(string: newValue!) attrString?.setAttributes([ NSAttributedString.Key.foregroundColor: NSUIColor.black, NSAttributedString.Key.font: NSUIFont.systemFont(ofSize: 12.0), NSAttributedString.Key.paragraphStyle: paragraphStyle ], range: NSMakeRange(0, attrString!.length)) } self.centerAttributedText = attrString } } /// the text that is displayed in the center of the pie-chart @objc open var centerAttributedText: NSAttributedString? { get { return _centerAttributedText } set { _centerAttributedText = newValue setNeedsDisplay() } } /// Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 @objc open var centerTextOffset: CGPoint { get { return _centerTextOffset } set { _centerTextOffset = newValue setNeedsDisplay() } } /// `true` if drawing the center text is enabled @objc open var drawCenterTextEnabled: Bool { get { return _drawCenterTextEnabled } set { _drawCenterTextEnabled = newValue setNeedsDisplay() } } /// - returns: `true` if drawing the center text is enabled @objc open var isDrawCenterTextEnabled: Bool { get { return drawCenterTextEnabled } } internal override var requiredLegendOffset: CGFloat { return _legend.font.pointSize * 2.0 } internal override var requiredBaseOffset: CGFloat { return 0.0 } open override var radius: CGFloat { return _circleBox.width / 2.0 } /// - returns: The circlebox, the boundingbox of the pie-chart slices @objc open var circleBox: CGRect { return _circleBox } /// - returns: The center of the circlebox @objc open var centerCircleBox: CGPoint { return CGPoint(x: _circleBox.midX, y: _circleBox.midY) } /// the radius of the hole in the center of the piechart in percent of the maximum radius (max = the radius of the whole chart) /// /// **default**: 0.5 (50%) (half the pie) @objc open var holeRadiusPercent: CGFloat { get { return _holeRadiusPercent } set { _holeRadiusPercent = newValue setNeedsDisplay() } } /// The color that the transparent-circle should have. /// /// **default**: `nil` @objc open var transparentCircleColor: NSUIColor? { get { return _transparentCircleColor } set { _transparentCircleColor = newValue setNeedsDisplay() } } /// the radius of the transparent circle that is drawn next to the hole in the piechart in percent of the maximum radius (max = the radius of the whole chart) /// /// **default**: 0.55 (55%) -> means 5% larger than the center-hole by default @objc open var transparentCircleRadiusPercent: CGFloat { get { return _transparentCircleRadiusPercent } set { _transparentCircleRadiusPercent = newValue setNeedsDisplay() } } /// The color the entry labels are drawn with. @objc open var entryLabelColor: NSUIColor? { get { return _entryLabelColor } set { _entryLabelColor = newValue setNeedsDisplay() } } /// The font the entry labels are drawn with. @objc open var entryLabelFont: NSUIFont? { get { return _entryLabelFont } set { _entryLabelFont = newValue setNeedsDisplay() } } /// Set this to true to draw the enrty labels into the pie slices @objc open var drawEntryLabelsEnabled: Bool { get { return _drawEntryLabelsEnabled } set { _drawEntryLabelsEnabled = newValue setNeedsDisplay() } } /// - returns: `true` if drawing entry labels is enabled, `false` ifnot @objc open var isDrawEntryLabelsEnabled: Bool { get { return drawEntryLabelsEnabled } } /// If this is enabled, values inside the PieChart are drawn in percent and not with their original value. Values provided for the ValueFormatter to format are then provided in percent. @objc open var usePercentValuesEnabled: Bool { get { return _usePercentValuesEnabled } set { _usePercentValuesEnabled = newValue setNeedsDisplay() } } /// - returns: `true` if drawing x-values is enabled, `false` ifnot @objc open var isUsePercentValuesEnabled: Bool { get { return usePercentValuesEnabled } } /// the rectangular radius of the bounding box for the center text, as a percentage of the pie hole @objc open var centerTextRadiusPercent: CGFloat { get { return _centerTextRadiusPercent } set { _centerTextRadiusPercent = newValue setNeedsDisplay() } } /// The max angle that is used for calculating the pie-circle. /// 360 means it's a full pie-chart, 180 results in a half-pie-chart. /// **default**: 360.0 @objc open var maxAngle: CGFloat { get { return _maxAngle } set { _maxAngle = newValue if _maxAngle > 360.0 { _maxAngle = 360.0 } if _maxAngle < 90.0 { _maxAngle = 90.0 } } } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/PieRadarChartViewBase.swift ================================================ // // PieRadarChartViewBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// Base class of PieChartView and RadarChartView. open class PieRadarChartViewBase: ChartViewBase { /// holds the normalized version of the current rotation angle of the chart private var _rotationAngle = CGFloat(270.0) /// holds the raw version of the current rotation angle of the chart private var _rawRotationAngle = CGFloat(270.0) /// flag that indicates if rotation is enabled or not @objc open var rotationEnabled = true /// Sets the minimum offset (padding) around the chart, defaults to 0.0 @objc open var minOffset = CGFloat(0.0) /// iOS && OSX only: Enabled multi-touch rotation using two fingers. private var _rotationWithTwoFingers = false private var _tapGestureRecognizer: NSUITapGestureRecognizer! #if !os(tvOS) private var _rotationGestureRecognizer: NSUIRotationGestureRecognizer! #endif public override init(frame: CGRect) { super.init(frame: frame) } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } deinit { stopDeceleration() } internal override func initialize() { super.initialize() _tapGestureRecognizer = NSUITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))) self.addGestureRecognizer(_tapGestureRecognizer) #if !os(tvOS) _rotationGestureRecognizer = NSUIRotationGestureRecognizer(target: self, action: #selector(rotationGestureRecognized(_:))) self.addGestureRecognizer(_rotationGestureRecognizer) _rotationGestureRecognizer.isEnabled = rotationWithTwoFingers #endif } internal override func calcMinMax() { /*_xAxis.axisRange = Double((_data?.xVals.count ?? 0) - 1)*/ } open override var maxVisibleCount: Int { get { return data?.entryCount ?? 0 } } open override func notifyDataSetChanged() { calcMinMax() if let data = _data , _legend !== nil { legendRenderer.computeLegend(data: data) } calculateOffsets() setNeedsDisplay() } internal override func calculateOffsets() { var legendLeft = CGFloat(0.0) var legendRight = CGFloat(0.0) var legendBottom = CGFloat(0.0) var legendTop = CGFloat(0.0) if _legend != nil && _legend.enabled && !_legend.drawInside { let fullLegendWidth = min(_legend.neededWidth, _viewPortHandler.chartWidth * _legend.maxSizePercent) switch _legend.orientation { case .vertical: var xLegendOffset: CGFloat = 0.0 if _legend.horizontalAlignment == .left || _legend.horizontalAlignment == .right { if _legend.verticalAlignment == .center { // this is the space between the legend and the chart let spacing = CGFloat(13.0) xLegendOffset = fullLegendWidth + spacing } else { // this is the space between the legend and the chart let spacing = CGFloat(8.0) let legendWidth = fullLegendWidth + spacing let legendHeight = _legend.neededHeight + _legend.textHeightMax let c = self.midPoint let bottomX = _legend.horizontalAlignment == .right ? self.bounds.width - legendWidth + 15.0 : legendWidth - 15.0 let bottomY = legendHeight + 15 let distLegend = distanceToCenter(x: bottomX, y: bottomY) let reference = getPosition(center: c, dist: self.radius, angle: angleForPoint(x: bottomX, y: bottomY)) let distReference = distanceToCenter(x: reference.x, y: reference.y) let minOffset = CGFloat(5.0) if bottomY >= c.y && self.bounds.height - legendWidth > self.bounds.width { xLegendOffset = legendWidth } else if distLegend < distReference { let diff = distReference - distLegend xLegendOffset = minOffset + diff } } } switch _legend.horizontalAlignment { case .left: legendLeft = xLegendOffset case .right: legendRight = xLegendOffset case .center: switch _legend.verticalAlignment { case .top: legendTop = min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) case .bottom: legendBottom = min(_legend.neededHeight, _viewPortHandler.chartHeight * _legend.maxSizePercent) default: break } } case .horizontal: var yLegendOffset: CGFloat = 0.0 if _legend.verticalAlignment == .top || _legend.verticalAlignment == .bottom { // It's possible that we do not need this offset anymore as it // is available through the extraOffsets, but changing it can mean // changing default visibility for existing apps. let yOffset = self.requiredLegendOffset yLegendOffset = min( _legend.neededHeight + yOffset, _viewPortHandler.chartHeight * _legend.maxSizePercent) } switch _legend.verticalAlignment { case .top: legendTop = yLegendOffset case .bottom: legendBottom = yLegendOffset default: break } } legendLeft += self.requiredBaseOffset legendRight += self.requiredBaseOffset legendTop += self.requiredBaseOffset legendBottom += self.requiredBaseOffset } legendTop += self.extraTopOffset legendRight += self.extraRightOffset legendBottom += self.extraBottomOffset legendLeft += self.extraLeftOffset var minOffset = self.minOffset if self is RadarChartView { let x = self.xAxis if x.isEnabled && x.drawLabelsEnabled { minOffset = max(minOffset, x.labelRotatedWidth) } } let offsetLeft = max(minOffset, legendLeft) let offsetTop = max(minOffset, legendTop) let offsetRight = max(minOffset, legendRight) let offsetBottom = max(minOffset, max(self.requiredBaseOffset, legendBottom)) _viewPortHandler.restrainViewPort(offsetLeft: offsetLeft, offsetTop: offsetTop, offsetRight: offsetRight, offsetBottom: offsetBottom) } /// - returns: The angle relative to the chart center for the given point on the chart in degrees. /// The angle is always between 0 and 360°, 0° is NORTH, 90° is EAST, ... @objc open func angleForPoint(x: CGFloat, y: CGFloat) -> CGFloat { let c = centerOffsets let tx = Double(x - c.x) let ty = Double(y - c.y) let length = sqrt(tx * tx + ty * ty) let r = acos(ty / length) var angle = r.RAD2DEG if x > c.x { angle = 360.0 - angle } // add 90° because chart starts EAST angle = angle + 90.0 // neutralize overflow if angle > 360.0 { angle = angle - 360.0 } return CGFloat(angle) } /// Calculates the position around a center point, depending on the distance /// from the center, and the angle of the position around the center. @objc open func getPosition(center: CGPoint, dist: CGFloat, angle: CGFloat) -> CGPoint { return CGPoint(x: center.x + dist * cos(angle.DEG2RAD), y: center.y + dist * sin(angle.DEG2RAD)) } /// - returns: The distance of a certain point on the chart to the center of the chart. @objc open func distanceToCenter(x: CGFloat, y: CGFloat) -> CGFloat { let c = self.centerOffsets var dist = CGFloat(0.0) var xDist = CGFloat(0.0) var yDist = CGFloat(0.0) if x > c.x { xDist = x - c.x } else { xDist = c.x - x } if y > c.y { yDist = y - c.y } else { yDist = c.y - y } // pythagoras dist = sqrt(pow(xDist, 2.0) + pow(yDist, 2.0)) return dist } /// - returns: The xIndex for the given angle around the center of the chart. /// -1 if not found / outofbounds. @objc open func indexForAngle(_ angle: CGFloat) -> Int { fatalError("indexForAngle() cannot be called on PieRadarChartViewBase") } /// current rotation angle of the pie chart /// /// **default**: 270 --> top (NORTH) /// - returns: Will always return a normalized value, which will be between 0.0 < 360.0 @objc open var rotationAngle: CGFloat { get { return _rotationAngle } set { _rawRotationAngle = newValue _rotationAngle = newValue.normalizedAngle setNeedsDisplay() } } /// gets the raw version of the current rotation angle of the pie chart the returned value could be any value, negative or positive, outside of the 360 degrees. /// this is used when working with rotation direction, mainly by gestures and animations. @objc open var rawRotationAngle: CGFloat { return _rawRotationAngle } /// - returns: The diameter of the pie- or radar-chart @objc open var diameter: CGFloat { var content = _viewPortHandler.contentRect content.origin.x += extraLeftOffset content.origin.y += extraTopOffset content.size.width -= extraLeftOffset + extraRightOffset content.size.height -= extraTopOffset + extraBottomOffset return min(content.width, content.height) } /// - returns: The radius of the chart in pixels. @objc open var radius: CGFloat { fatalError("radius cannot be called on PieRadarChartViewBase") } /// - returns: The required offset for the chart legend. internal var requiredLegendOffset: CGFloat { fatalError("requiredLegendOffset cannot be called on PieRadarChartViewBase") } /// - returns: The base offset needed for the chart without calculating the /// legend size. internal var requiredBaseOffset: CGFloat { fatalError("requiredBaseOffset cannot be called on PieRadarChartViewBase") } open override var chartYMax: Double { return 0.0 } open override var chartYMin: Double { return 0.0 } @objc open var isRotationEnabled: Bool { return rotationEnabled } /// flag that indicates if rotation is done with two fingers or one. /// when the chart is inside a scrollview, you need a two-finger rotation because a one-finger rotation eats up all touch events. /// /// On iOS this will disable one-finger rotation. /// On OSX this will keep two-finger multitouch rotation, and one-pointer mouse rotation. /// /// **default**: false @objc open var rotationWithTwoFingers: Bool { get { return _rotationWithTwoFingers } set { _rotationWithTwoFingers = newValue #if !os(tvOS) _rotationGestureRecognizer.isEnabled = _rotationWithTwoFingers #endif } } /// flag that indicates if rotation is done with two fingers or one. /// when the chart is inside a scrollview, you need a two-finger rotation because a one-finger rotation eats up all touch events. /// /// On iOS this will disable one-finger rotation. /// On OSX this will keep two-finger multitouch rotation, and one-pointer mouse rotation. /// /// **default**: false @objc open var isRotationWithTwoFingers: Bool { return _rotationWithTwoFingers } // MARK: - Animation private var _spinAnimator: Animator! /// Applys a spin animation to the Chart. @objc open func spin(duration: TimeInterval, fromAngle: CGFloat, toAngle: CGFloat, easing: ChartEasingFunctionBlock?) { if _spinAnimator != nil { _spinAnimator.stop() } _spinAnimator = Animator() _spinAnimator.updateBlock = { self.rotationAngle = (toAngle - fromAngle) * CGFloat(self._spinAnimator.phaseX) + fromAngle } _spinAnimator.stopBlock = { self._spinAnimator = nil } _spinAnimator.animate(xAxisDuration: duration, easing: easing) } @objc open func spin(duration: TimeInterval, fromAngle: CGFloat, toAngle: CGFloat, easingOption: ChartEasingOption) { spin(duration: duration, fromAngle: fromAngle, toAngle: toAngle, easing: easingFunctionFromOption(easingOption)) } @objc open func spin(duration: TimeInterval, fromAngle: CGFloat, toAngle: CGFloat) { spin(duration: duration, fromAngle: fromAngle, toAngle: toAngle, easing: nil) } @objc open func stopSpinAnimation() { if _spinAnimator != nil { _spinAnimator.stop() } } // MARK: - Gestures private var _rotationGestureStartPoint: CGPoint! private var _isRotating = false private var _startAngle = CGFloat(0.0) private struct AngularVelocitySample { var time: TimeInterval var angle: CGFloat } private var _velocitySamples = [AngularVelocitySample]() private var _decelerationLastTime: TimeInterval = 0.0 private var _decelerationDisplayLink: NSUIDisplayLink! private var _decelerationAngularVelocity: CGFloat = 0.0 internal final func processRotationGestureBegan(location: CGPoint) { self.resetVelocity() if rotationEnabled { self.sampleVelocity(touchLocation: location) } self.setGestureStartAngle(x: location.x, y: location.y) _rotationGestureStartPoint = location } internal final func processRotationGestureMoved(location: CGPoint) { if isDragDecelerationEnabled { sampleVelocity(touchLocation: location) } if !_isRotating && distance( eventX: location.x, startX: _rotationGestureStartPoint.x, eventY: location.y, startY: _rotationGestureStartPoint.y) > CGFloat(8.0) { _isRotating = true } else { self.updateGestureRotation(x: location.x, y: location.y) setNeedsDisplay() } } internal final func processRotationGestureEnded(location: CGPoint) { if isDragDecelerationEnabled { stopDeceleration() sampleVelocity(touchLocation: location) _decelerationAngularVelocity = calculateVelocity() if _decelerationAngularVelocity != 0.0 { _decelerationLastTime = CACurrentMediaTime() _decelerationDisplayLink = NSUIDisplayLink(target: self, selector: #selector(PieRadarChartViewBase.decelerationLoop)) _decelerationDisplayLink.add(to: RunLoop.main, forMode: .common) } } } internal final func processRotationGestureCancelled() { if _isRotating { _isRotating = false } } #if !os(OSX) open override func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) { // if rotation by touch is enabled if rotationEnabled { stopDeceleration() if !rotationWithTwoFingers, let touchLocation = touches.first?.location(in: self) { processRotationGestureBegan(location: touchLocation) } } if !_isRotating { super.nsuiTouchesBegan(touches, withEvent: event) } } open override func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) { if rotationEnabled && !rotationWithTwoFingers, let touch = touches.first { let touchLocation = touch.location(in: self) processRotationGestureMoved(location: touchLocation) } if !_isRotating { super.nsuiTouchesMoved(touches, withEvent: event) } } open override func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) { if !_isRotating { super.nsuiTouchesEnded(touches, withEvent: event) } if rotationEnabled && !rotationWithTwoFingers, let touch = touches.first { let touchLocation = touch.location(in: self) processRotationGestureEnded(location: touchLocation) } if _isRotating { _isRotating = false } } open override func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) { super.nsuiTouchesCancelled(touches, withEvent: event) processRotationGestureCancelled() } #endif #if os(OSX) open override func mouseDown(with theEvent: NSEvent) { // if rotation by touch is enabled if rotationEnabled { stopDeceleration() let location = self.convert(theEvent.locationInWindow, from: nil) processRotationGestureBegan(location: location) } if !_isRotating { super.mouseDown(with: theEvent) } } open override func mouseDragged(with theEvent: NSEvent) { if rotationEnabled { let location = self.convert(theEvent.locationInWindow, from: nil) processRotationGestureMoved(location: location) } if !_isRotating { super.mouseDragged(with: theEvent) } } open override func mouseUp(with theEvent: NSEvent) { if !_isRotating { super.mouseUp(with: theEvent) } if rotationEnabled { let location = self.convert(theEvent.locationInWindow, from: nil) processRotationGestureEnded(location: location) } if _isRotating { _isRotating = false } } #endif private func resetVelocity() { _velocitySamples.removeAll(keepingCapacity: false) } private func sampleVelocity(touchLocation: CGPoint) { let currentTime = CACurrentMediaTime() _velocitySamples.append(AngularVelocitySample(time: currentTime, angle: angleForPoint(x: touchLocation.x, y: touchLocation.y))) // Remove samples older than our sample time - 1 seconds var i = 0, count = _velocitySamples.count while (i < count - 2) { if currentTime - _velocitySamples[i].time > 1.0 { _velocitySamples.remove(at: 0) i -= 1 count -= 1 } else { break } i += 1 } } private func calculateVelocity() -> CGFloat { if _velocitySamples.isEmpty { return 0.0 } var firstSample = _velocitySamples[0] var lastSample = _velocitySamples[_velocitySamples.count - 1] // Look for a sample that's closest to the latest sample, but not the same, so we can deduce the direction var beforeLastSample = firstSample for i in stride(from: (_velocitySamples.count - 1), through: 0, by: -1) { beforeLastSample = _velocitySamples[i] if beforeLastSample.angle != lastSample.angle { break } } // Calculate the sampling time var timeDelta = lastSample.time - firstSample.time if timeDelta == 0.0 { timeDelta = 0.1 } // Calculate clockwise/ccw by choosing two values that should be closest to each other, // so if the angles are two far from each other we know they are inverted "for sure" var clockwise = lastSample.angle >= beforeLastSample.angle if (abs(lastSample.angle - beforeLastSample.angle) > 270.0) { clockwise = !clockwise } // Now if the "gesture" is over a too big of an angle - then we know the angles are inverted, and we need to move them closer to each other from both sides of the 360.0 wrapping point if lastSample.angle - firstSample.angle > 180.0 { firstSample.angle += 360.0 } else if firstSample.angle - lastSample.angle > 180.0 { lastSample.angle += 360.0 } // The velocity var velocity = abs((lastSample.angle - firstSample.angle) / CGFloat(timeDelta)) // Direction? if !clockwise { velocity = -velocity } return velocity } /// sets the starting angle of the rotation, this is only used by the touch listener, x and y is the touch position private func setGestureStartAngle(x: CGFloat, y: CGFloat) { _startAngle = angleForPoint(x: x, y: y) // take the current angle into consideration when starting a new drag _startAngle -= _rotationAngle } /// updates the view rotation depending on the given touch position, also takes the starting angle into consideration private func updateGestureRotation(x: CGFloat, y: CGFloat) { self.rotationAngle = angleForPoint(x: x, y: y) - _startAngle } @objc open func stopDeceleration() { if _decelerationDisplayLink !== nil { _decelerationDisplayLink.remove(from: RunLoop.main, forMode: .common) _decelerationDisplayLink = nil } } @objc private func decelerationLoop() { let currentTime = CACurrentMediaTime() _decelerationAngularVelocity *= self.dragDecelerationFrictionCoef let timeInterval = CGFloat(currentTime - _decelerationLastTime) self.rotationAngle += _decelerationAngularVelocity * timeInterval _decelerationLastTime = currentTime if(abs(_decelerationAngularVelocity) < 0.001) { stopDeceleration() } } /// - returns: The distance between two points private func distance(eventX: CGFloat, startX: CGFloat, eventY: CGFloat, startY: CGFloat) -> CGFloat { let dx = eventX - startX let dy = eventY - startY return sqrt(dx * dx + dy * dy) } /// - returns: The distance between two points private func distance(from: CGPoint, to: CGPoint) -> CGFloat { let dx = from.x - to.x let dy = from.y - to.y return sqrt(dx * dx + dy * dy) } /// reference to the last highlighted object private var _lastHighlight: Highlight! @objc private func tapGestureRecognized(_ recognizer: NSUITapGestureRecognizer) { if recognizer.state == NSUIGestureRecognizerState.ended { if !self.isHighLightPerTapEnabled { return } let location = recognizer.location(in: self) let high = self.getHighlightByTouchPoint(location) self.highlightValue(high, callDelegate: true) } } #if !os(tvOS) @objc private func rotationGestureRecognized(_ recognizer: NSUIRotationGestureRecognizer) { if recognizer.state == NSUIGestureRecognizerState.began { stopDeceleration() _startAngle = self.rawRotationAngle } if recognizer.state == NSUIGestureRecognizerState.began || recognizer.state == NSUIGestureRecognizerState.changed { let angle = recognizer.nsuiRotation.RAD2DEG self.rotationAngle = _startAngle + angle setNeedsDisplay() } else if recognizer.state == NSUIGestureRecognizerState.ended { let angle = recognizer.nsuiRotation.RAD2DEG self.rotationAngle = _startAngle + angle setNeedsDisplay() if isDragDecelerationEnabled { stopDeceleration() _decelerationAngularVelocity = recognizer.velocity.RAD2DEG if _decelerationAngularVelocity != 0.0 { _decelerationLastTime = CACurrentMediaTime() _decelerationDisplayLink = NSUIDisplayLink(target: self, selector: #selector(PieRadarChartViewBase.decelerationLoop)) _decelerationDisplayLink.add(to: RunLoop.main, forMode: .common) } } } } #endif } ================================================ FILE: Pods/Charts/Source/Charts/Charts/RadarChartView.swift ================================================ // // RadarChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Implementation of the RadarChart, a "spidernet"-like chart. It works best /// when displaying 5-10 entries per DataSet. open class RadarChartView: PieRadarChartViewBase { /// width of the web lines that come from the center. @objc open var webLineWidth = CGFloat(1.5) /// width of the web lines that are in between the lines coming from the center @objc open var innerWebLineWidth = CGFloat(0.75) /// color for the web lines that come from the center @objc open var webColor = NSUIColor(red: 122/255.0, green: 122/255.0, blue: 122.0/255.0, alpha: 1.0) /// color for the web lines in between the lines that come from the center. @objc open var innerWebColor = NSUIColor(red: 122/255.0, green: 122/255.0, blue: 122.0/255.0, alpha: 1.0) /// transparency the grid is drawn with (0.0 - 1.0) @objc open var webAlpha: CGFloat = 150.0 / 255.0 /// flag indicating if the web lines should be drawn or not @objc open var drawWeb = true /// modulus that determines how many labels and web-lines are skipped before the next is drawn private var _skipWebLineCount = 0 /// the object reprsenting the y-axis labels private var _yAxis: YAxis! internal var _yAxisRenderer: YAxisRendererRadarChart! internal var _xAxisRenderer: XAxisRendererRadarChart! public override init(frame: CGRect) { super.init(frame: frame) } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } internal override func initialize() { super.initialize() _yAxis = YAxis(position: .left) renderer = RadarChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler) _yAxisRenderer = YAxisRendererRadarChart(viewPortHandler: _viewPortHandler, yAxis: _yAxis, chart: self) _xAxisRenderer = XAxisRendererRadarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, chart: self) self.highlighter = RadarHighlighter(chart: self) } internal override func calcMinMax() { super.calcMinMax() guard let data = _data else { return } _yAxis.calculate(min: data.getYMin(axis: .left), max: data.getYMax(axis: .left)) _xAxis.calculate(min: 0.0, max: Double(data.maxEntryCountSet?.entryCount ?? 0)) } open override func notifyDataSetChanged() { calcMinMax() _yAxisRenderer?.computeAxis(min: _yAxis._axisMinimum, max: _yAxis._axisMaximum, inverted: _yAxis.isInverted) _xAxisRenderer?.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) if let data = _data, let legend = _legend, !legend.isLegendCustom { legendRenderer?.computeLegend(data: data) } calculateOffsets() setNeedsDisplay() } open override func draw(_ rect: CGRect) { super.draw(rect) guard data != nil, let renderer = renderer else { return } let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext else { return } if _xAxis.isEnabled { _xAxisRenderer.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) } _xAxisRenderer?.renderAxisLabels(context: context) if drawWeb { renderer.drawExtras(context: context) } if _yAxis.isEnabled && _yAxis.isDrawLimitLinesBehindDataEnabled { _yAxisRenderer.renderLimitLines(context: context) } renderer.drawData(context: context) if valuesToHighlight() { renderer.drawHighlighted(context: context, indices: _indicesToHighlight) } if _yAxis.isEnabled && !_yAxis.isDrawLimitLinesBehindDataEnabled { _yAxisRenderer.renderLimitLines(context: context) } _yAxisRenderer.renderAxisLabels(context: context) renderer.drawValues(context: context) legendRenderer.renderLegend(context: context) drawDescription(context: context) drawMarkers(context: context) } /// - returns: The factor that is needed to transform values into pixels. @objc open var factor: CGFloat { let content = _viewPortHandler.contentRect return min(content.width / 2.0, content.height / 2.0) / CGFloat(_yAxis.axisRange) } /// - returns: The angle that each slice in the radar chart occupies. @objc open var sliceAngle: CGFloat { return 360.0 / CGFloat(_data?.maxEntryCountSet?.entryCount ?? 0) } open override func indexForAngle(_ angle: CGFloat) -> Int { // take the current angle of the chart into consideration let a = (angle - self.rotationAngle).normalizedAngle let sliceAngle = self.sliceAngle let max = _data?.maxEntryCountSet?.entryCount ?? 0 var index = 0 for i in 0.. a { index = i break } } return index } /// - returns: The object that represents all y-labels of the RadarChart. @objc open var yAxis: YAxis { return _yAxis } /// Sets the number of web-lines that should be skipped on chart web before the next one is drawn. This targets the lines that come from the center of the RadarChart. /// if count = 1 -> 1 line is skipped in between @objc open var skipWebLineCount: Int { get { return _skipWebLineCount } set { _skipWebLineCount = max(0, newValue) } } internal override var requiredLegendOffset: CGFloat { return _legend.font.pointSize * 4.0 } internal override var requiredBaseOffset: CGFloat { return _xAxis.isEnabled && _xAxis.isDrawLabelsEnabled ? _xAxis.labelRotatedWidth : 10.0 } open override var radius: CGFloat { let content = _viewPortHandler.contentRect return min(content.width / 2.0, content.height / 2.0) } /// - returns: The maximum value this chart can display on it's y-axis. open override var chartYMax: Double { return _yAxis._axisMaximum } /// - returns: The minimum value this chart can display on it's y-axis. open override var chartYMin: Double { return _yAxis._axisMinimum } /// - returns: The range of y-values this chart can display. @objc open var yRange: Double { return _yAxis.axisRange } } ================================================ FILE: Pods/Charts/Source/Charts/Charts/ScatterChartView.swift ================================================ // // ScatterChartView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// The ScatterChart. Draws dots, triangles, squares and custom shapes into the chartview. open class ScatterChartView: BarLineChartViewBase, ScatterChartDataProvider { open override func initialize() { super.initialize() renderer = ScatterChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) xAxis.spaceMin = 0.5 xAxis.spaceMax = 0.5 } // MARK: - ScatterChartDataProvider open var scatterData: ScatterChartData? { return _data as? ScatterChartData } } ================================================ FILE: Pods/Charts/Source/Charts/Components/AxisBase.swift ================================================ // // AxisBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Base class for all axes @objc(ChartAxisBase) open class AxisBase: ComponentBase { public override init() { super.init() } /// Custom formatter that is used instead of the auto-formatter if set private var _axisValueFormatter: IAxisValueFormatter? @objc open var labelFont = NSUIFont.systemFont(ofSize: 10.0) @objc open var labelTextColor = NSUIColor.black @objc open var axisLineColor = NSUIColor.gray @objc open var axisLineWidth = CGFloat(0.5) @objc open var axisLineDashPhase = CGFloat(0.0) @objc open var axisLineDashLengths: [CGFloat]! @objc open var gridColor = NSUIColor.gray.withAlphaComponent(0.9) @objc open var gridLineWidth = CGFloat(0.5) @objc open var gridLineDashPhase = CGFloat(0.0) @objc open var gridLineDashLengths: [CGFloat]! @objc open var gridLineCap = CGLineCap.butt @objc open var drawGridLinesEnabled = true @objc open var drawAxisLineEnabled = true /// flag that indicates of the labels of this axis should be drawn or not @objc open var drawLabelsEnabled = true private var _centerAxisLabelsEnabled = false /// Centers the axis labels instead of drawing them at their original position. /// This is useful especially for grouped BarChart. @objc open var centerAxisLabelsEnabled: Bool { get { return _centerAxisLabelsEnabled && entryCount > 0 } set { _centerAxisLabelsEnabled = newValue } } @objc open var isCenterAxisLabelsEnabled: Bool { get { return centerAxisLabelsEnabled } } /// array of limitlines that can be set for the axis private var _limitLines = [ChartLimitLine]() /// Are the LimitLines drawn behind the data or in front of the data? /// /// **default**: false @objc open var drawLimitLinesBehindDataEnabled = false /// the flag can be used to turn off the antialias for grid lines @objc open var gridAntialiasEnabled = true /// the actual array of entries @objc open var entries = [Double]() /// axis label entries only used for centered labels @objc open var centeredEntries = [Double]() /// the number of entries the legend contains @objc open var entryCount: Int { return entries.count } /// the number of label entries the axis should have /// /// **default**: 6 private var _labelCount = Int(6) /// the number of decimal digits to use (for the default formatter @objc open var decimals: Int = 0 /// When true, axis labels are controlled by the `granularity` property. /// When false, axis values could possibly be repeated. /// This could happen if two adjacent axis values are rounded to same value. /// If using granularity this could be avoided by having fewer axis values visible. @objc open var granularityEnabled = false private var _granularity = Double(1.0) /// The minimum interval between axis values. /// This can be used to avoid label duplicating when zooming in. /// /// **default**: 1.0 @objc open var granularity: Double { get { return _granularity } set { _granularity = newValue // set this to `true` if it was disabled, as it makes no sense to set this property with granularity disabled granularityEnabled = true } } /// The minimum interval between axis values. @objc open var isGranularityEnabled: Bool { get { return granularityEnabled } } /// if true, the set number of y-labels will be forced @objc open var forceLabelsEnabled = false @objc open func getLongestLabel() -> String { var longest = "" for i in 0 ..< entries.count { let text = getFormattedLabel(i) if longest.count < text.count { longest = text } } return longest } /// - returns: The formatted label at the specified index. This will either use the auto-formatter or the custom formatter (if one is set). @objc open func getFormattedLabel(_ index: Int) -> String { if index < 0 || index >= entries.count { return "" } return valueFormatter?.stringForValue(entries[index], axis: self) ?? "" } /// Sets the formatter to be used for formatting the axis labels. /// If no formatter is set, the chart will automatically determine a reasonable formatting (concerning decimals) for all the values that are drawn inside the chart. /// Use `nil` to use the formatter calculated by the chart. @objc open var valueFormatter: IAxisValueFormatter? { get { if _axisValueFormatter == nil || (_axisValueFormatter is DefaultAxisValueFormatter && (_axisValueFormatter as! DefaultAxisValueFormatter).hasAutoDecimals && (_axisValueFormatter as! DefaultAxisValueFormatter).decimals != decimals) { _axisValueFormatter = DefaultAxisValueFormatter(decimals: decimals) } return _axisValueFormatter } set { _axisValueFormatter = newValue ?? DefaultAxisValueFormatter(decimals: decimals) } } @objc open var isDrawGridLinesEnabled: Bool { return drawGridLinesEnabled } @objc open var isDrawAxisLineEnabled: Bool { return drawAxisLineEnabled } @objc open var isDrawLabelsEnabled: Bool { return drawLabelsEnabled } /// Are the LimitLines drawn behind the data or in front of the data? /// /// **default**: false @objc open var isDrawLimitLinesBehindDataEnabled: Bool { return drawLimitLinesBehindDataEnabled } /// Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` @objc open var spaceMin: Double = 0.0 /// Extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` @objc open var spaceMax: Double = 0.0 /// Flag indicating that the axis-min value has been customized internal var _customAxisMin: Bool = false /// Flag indicating that the axis-max value has been customized internal var _customAxisMax: Bool = false /// Do not touch this directly, instead, use axisMinimum. /// This is automatically calculated to represent the real min value, /// and is used when calculating the effective minimum. internal var _axisMinimum = Double(0) /// Do not touch this directly, instead, use axisMaximum. /// This is automatically calculated to represent the real max value, /// and is used when calculating the effective maximum. internal var _axisMaximum = Double(0) /// the total range of values this axis covers @objc open var axisRange = Double(0) /// The minumum number of labels on the axis @objc open var axisMinLabels = Int(2) { didSet { axisMinLabels = axisMinLabels > 0 ? axisMinLabels : oldValue } } /// The maximum number of labels on the axis @objc open var axisMaxLabels = Int(25) { didSet { axisMinLabels = axisMaxLabels > 0 ? axisMaxLabels : oldValue } } /// the number of label entries the axis should have /// max = 25, /// min = 2, /// default = 6, /// be aware that this number is not fixed and can only be approximated @objc open var labelCount: Int { get { return _labelCount } set { _labelCount = newValue if _labelCount > axisMaxLabels { _labelCount = axisMaxLabels } if _labelCount < axisMinLabels { _labelCount = axisMinLabels } forceLabelsEnabled = false } } @objc open func setLabelCount(_ count: Int, force: Bool) { self.labelCount = count forceLabelsEnabled = force } /// - returns: `true` if focing the y-label count is enabled. Default: false @objc open var isForceLabelsEnabled: Bool { return forceLabelsEnabled } /// Adds a new ChartLimitLine to this axis. @objc open func addLimitLine(_ line: ChartLimitLine) { _limitLines.append(line) } /// Removes the specified ChartLimitLine from the axis. @objc open func removeLimitLine(_ line: ChartLimitLine) { for i in 0 ..< _limitLines.count { if _limitLines[i] === line { _limitLines.remove(at: i) return } } } /// Removes all LimitLines from the axis. @objc open func removeAllLimitLines() { _limitLines.removeAll(keepingCapacity: false) } /// - returns: The LimitLines of this axis. @objc open var limitLines : [ChartLimitLine] { return _limitLines } // MARK: Custom axis ranges /// By calling this method, any custom minimum value that has been previously set is reseted, and the calculation is done automatically. @objc open func resetCustomAxisMin() { _customAxisMin = false } @objc open var isAxisMinCustom: Bool { return _customAxisMin } /// By calling this method, any custom maximum value that has been previously set is reseted, and the calculation is done automatically. @objc open func resetCustomAxisMax() { _customAxisMax = false } @objc open var isAxisMaxCustom: Bool { return _customAxisMax } /// The minimum value for this axis. /// If set, this value will not be calculated automatically depending on the provided data. /// Use `resetCustomAxisMin()` to undo this. @objc open var axisMinimum: Double { get { return _axisMinimum } set { _customAxisMin = true _axisMinimum = newValue axisRange = abs(_axisMaximum - newValue) } } /// The maximum value for this axis. /// If set, this value will not be calculated automatically depending on the provided data. /// Use `resetCustomAxisMax()` to undo this. @objc open var axisMaximum: Double { get { return _axisMaximum } set { _customAxisMax = true _axisMaximum = newValue axisRange = abs(newValue - _axisMinimum) } } /// Calculates the minimum, maximum and range values of the YAxis with the given minimum and maximum values from the chart data. /// - parameter dataMin: the y-min value according to chart data /// - parameter dataMax: the y-max value according to chart @objc open func calculate(min dataMin: Double, max dataMax: Double) { // if custom, use value as is, else use data value var min = _customAxisMin ? _axisMinimum : (dataMin - spaceMin) var max = _customAxisMax ? _axisMaximum : (dataMax + spaceMax) // temporary range (before calculations) let range = abs(max - min) // in case all values are equal if range == 0.0 { max = max + 1.0 min = min - 1.0 } _axisMinimum = min _axisMaximum = max // actual range axisRange = abs(max - min) } } ================================================ FILE: Pods/Charts/Source/Charts/Components/ChartLimitLine.swift ================================================ // // ChartLimitLine.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// The limit line is an additional feature for all Line, Bar and ScatterCharts. /// It allows the displaying of an additional line in the chart that marks a certain maximum / limit on the specified axis (x- or y-axis). open class ChartLimitLine: ComponentBase { @objc(ChartLimitLabelPosition) public enum LabelPosition: Int { case leftTop case leftBottom case rightTop case rightBottom } /// limit / maximum (the y-value or xIndex) @objc open var limit = Double(0.0) private var _lineWidth = CGFloat(2.0) @objc open var lineColor = NSUIColor(red: 237.0/255.0, green: 91.0/255.0, blue: 91.0/255.0, alpha: 1.0) @objc open var lineDashPhase = CGFloat(0.0) @objc open var lineDashLengths: [CGFloat]? @objc open var valueTextColor = NSUIColor.black @objc open var valueFont = NSUIFont.systemFont(ofSize: 13.0) @objc open var drawLabelEnabled = true @objc open var label = "" @objc open var labelPosition = LabelPosition.rightTop public override init() { super.init() } @objc public init(limit: Double) { super.init() self.limit = limit } @objc public init(limit: Double, label: String) { super.init() self.limit = limit self.label = label } /// set the line width of the chart (min = 0.2, max = 12); default 2 @objc open var lineWidth: CGFloat { get { return _lineWidth } set { if newValue < 0.2 { _lineWidth = 0.2 } else if newValue > 12.0 { _lineWidth = 12.0 } else { _lineWidth = newValue } } } } ================================================ FILE: Pods/Charts/Source/Charts/Components/ComponentBase.swift ================================================ // // ComponentBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// This class encapsulates everything both Axis, Legend and LimitLines have in common @objc(ChartComponentBase) open class ComponentBase: NSObject { /// flag that indicates if this component is enabled or not @objc open var enabled = true /// The offset this component has on the x-axis /// **default**: 5.0 @objc open var xOffset = CGFloat(5.0) /// The offset this component has on the x-axis /// **default**: 5.0 (or 0.0 on ChartYAxis) @objc open var yOffset = CGFloat(5.0) public override init() { super.init() } @objc open var isEnabled: Bool { return enabled } } ================================================ FILE: Pods/Charts/Source/Charts/Components/Description.swift ================================================ // // Description.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartDescription) open class Description: ComponentBase { public override init() { #if os(tvOS) // 23 is the smallest recommended font size on the TV font = NSUIFont.systemFont(ofSize: 23) #elseif os(OSX) font = NSUIFont.systemFont(ofSize: NSUIFont.systemFontSize) #else font = NSUIFont.systemFont(ofSize: 8.0) #endif super.init() } /// The text to be shown as the description. @objc open var text: String? = "Description Label" /// Custom position for the description text in pixels on the screen. open var position: CGPoint? = nil /// The text alignment of the description text. Default RIGHT. @objc open var textAlign: NSTextAlignment = NSTextAlignment.right /// Font object used for drawing the description text. @objc open var font: NSUIFont /// Text color used for drawing the description text @objc open var textColor = NSUIColor.black } ================================================ FILE: Pods/Charts/Source/Charts/Components/IMarker.swift ================================================ // // ChartMarker.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(IChartMarker) public protocol IMarker: class { /// - returns: The desired (general) offset you wish the IMarker to have on the x-axis. /// /// By returning x: -(width / 2) you will center the IMarker horizontally. /// /// By returning y: -(height / 2) you will center the IMarker vertically. var offset: CGPoint { get } /// - returns: The offset for drawing at the specific `point`. /// This allows conditional adjusting of the Marker position. /// If you have no adjustments to make, return self.offset(). /// /// - parameter point: This is the point at which the marker wants to be drawn. You can adjust the offset conditionally based on this argument. func offsetForDrawing(atPoint: CGPoint) -> CGPoint /// This method enables a custom IMarker to update it's content every time the IMarker is redrawn according to the data entry it points to. /// /// - parameter entry: The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or CandleEntry, simply cast it at runtime. /// - parameter highlight: The highlight object contains information about the highlighted value such as it's dataset-index, the selected range or stack-index (only stacked bar entries). func refreshContent(entry: ChartDataEntry, highlight: Highlight) /// Draws the IMarker on the given position on the given context func draw(context: CGContext, point: CGPoint) } ================================================ FILE: Pods/Charts/Source/Charts/Components/Legend.swift ================================================ // // Legend.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartLegend) open class Legend: ComponentBase { @objc(ChartLegendForm) public enum Form: Int { /// Avoid drawing a form case none /// Do not draw the a form, but leave space for it case empty /// Use default (default dataset's form to the legend's form) case `default` /// Draw a square case square /// Draw a circle case circle /// Draw a horizontal line case line } @objc(ChartLegendHorizontalAlignment) public enum HorizontalAlignment: Int { case left case center case right } @objc(ChartLegendVerticalAlignment) public enum VerticalAlignment: Int { case top case center case bottom } @objc(ChartLegendOrientation) public enum Orientation: Int { case horizontal case vertical } @objc(ChartLegendDirection) public enum Direction: Int { case leftToRight case rightToLeft } /// The legend entries array @objc open var entries = [LegendEntry]() /// Entries that will be appended to the end of the auto calculated entries after calculating the legend. /// (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect) @objc open var extraEntries = [LegendEntry]() /// Are the legend labels/colors a custom value or auto calculated? If false, then it's auto, if true, then custom. /// /// **default**: false (automatic legend) private var _isLegendCustom = false /// The horizontal alignment of the legend @objc open var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.left /// The vertical alignment of the legend @objc open var verticalAlignment: VerticalAlignment = VerticalAlignment.bottom /// The orientation of the legend @objc open var orientation: Orientation = Orientation.horizontal /// Flag indicating whether the legend will draw inside the chart or outside @objc open var drawInside: Bool = false /// Flag indicating whether the legend will draw inside the chart or outside @objc open var isDrawInsideEnabled: Bool { return drawInside } /// The text direction of the legend @objc open var direction: Direction = Direction.leftToRight @objc open var font: NSUIFont = NSUIFont.systemFont(ofSize: 10.0) @objc open var textColor = NSUIColor.black /// The form/shape of the legend forms @objc open var form = Form.square /// The size of the legend forms @objc open var formSize = CGFloat(8.0) /// The line width for forms that consist of lines @objc open var formLineWidth = CGFloat(3.0) /// Line dash configuration for shapes that consist of lines. /// /// This is how much (in pixels) into the dash pattern are we starting from. @objc open var formLineDashPhase: CGFloat = 0.0 /// Line dash configuration for shapes that consist of lines. /// /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] @objc open var formLineDashLengths: [CGFloat]? @objc open var xEntrySpace = CGFloat(6.0) @objc open var yEntrySpace = CGFloat(0.0) @objc open var formToTextSpace = CGFloat(5.0) @objc open var stackSpace = CGFloat(3.0) @objc open var calculatedLabelSizes = [CGSize]() @objc open var calculatedLabelBreakPoints = [Bool]() @objc open var calculatedLineSizes = [CGSize]() public override init() { super.init() self.xOffset = 5.0 self.yOffset = 3.0 } @objc public init(entries: [LegendEntry]) { super.init() self.entries = entries } @objc open func getMaximumEntrySize(withFont font: NSUIFont) -> CGSize { var maxW = CGFloat(0.0) var maxH = CGFloat(0.0) var maxFormSize: CGFloat = 0.0 for entry in entries { let formSize = entry.formSize.isNaN ? self.formSize : entry.formSize if formSize > maxFormSize { maxFormSize = formSize } guard let label = entry.label else { continue } let size = (label as NSString).size(withAttributes: [.font: font]) if size.width > maxW { maxW = size.width } if size.height > maxH { maxH = size.height } } return CGSize( width: maxW + maxFormSize + formToTextSpace, height: maxH ) } @objc open var neededWidth = CGFloat(0.0) @objc open var neededHeight = CGFloat(0.0) @objc open var textWidthMax = CGFloat(0.0) @objc open var textHeightMax = CGFloat(0.0) /// flag that indicates if word wrapping is enabled /// this is currently supported only for `orientation == Horizontal`. /// you may want to set maxSizePercent when word wrapping, to set the point where the text wraps. /// /// **default**: true @objc open var wordWrapEnabled = true /// if this is set, then word wrapping the legend is enabled. @objc open var isWordWrapEnabled: Bool { return wordWrapEnabled } /// The maximum relative size out of the whole chart view in percent. /// If the legend is to the right/left of the chart, then this affects the width of the legend. /// If the legend is to the top/bottom of the chart, then this affects the height of the legend. /// /// **default**: 0.95 (95%) @objc open var maxSizePercent: CGFloat = 0.95 @objc open func calculateDimensions(labelFont: NSUIFont, viewPortHandler: ViewPortHandler) { let maxEntrySize = getMaximumEntrySize(withFont: labelFont) let defaultFormSize = self.formSize let stackSpace = self.stackSpace let formToTextSpace = self.formToTextSpace let xEntrySpace = self.xEntrySpace let yEntrySpace = self.yEntrySpace let wordWrapEnabled = self.wordWrapEnabled let entries = self.entries let entryCount = entries.count textWidthMax = maxEntrySize.width textHeightMax = maxEntrySize.height switch orientation { case .vertical: var maxWidth = CGFloat(0.0) var width = CGFloat(0.0) var maxHeight = CGFloat(0.0) let labelLineHeight = labelFont.lineHeight var wasStacked = false for i in 0 ..< entryCount { let e = entries[i] let drawingForm = e.form != .none let formSize = e.formSize.isNaN ? defaultFormSize : e.formSize let label = e.label if !wasStacked { width = 0.0 } if drawingForm { if wasStacked { width += stackSpace } width += formSize } if label != nil { let size = (label! as NSString).size(withAttributes: [.font: labelFont]) if drawingForm && !wasStacked { width += formToTextSpace } else if wasStacked { maxWidth = max(maxWidth, width) maxHeight += labelLineHeight + yEntrySpace width = 0.0 wasStacked = false } width += size.width if i < entryCount - 1 { maxHeight += labelLineHeight + yEntrySpace } } else { wasStacked = true width += formSize if i < entryCount - 1 { width += stackSpace } } maxWidth = max(maxWidth, width) } neededWidth = maxWidth neededHeight = maxHeight case .horizontal: let labelLineHeight = labelFont.lineHeight let contentWidth: CGFloat = viewPortHandler.contentWidth * maxSizePercent // Prepare arrays for calculated layout if calculatedLabelSizes.count != entryCount { calculatedLabelSizes = [CGSize](repeating: CGSize(), count: entryCount) } if calculatedLabelBreakPoints.count != entryCount { calculatedLabelBreakPoints = [Bool](repeating: false, count: entryCount) } calculatedLineSizes.removeAll(keepingCapacity: true) // Start calculating layout let labelAttrs = [NSAttributedString.Key.font: labelFont] var maxLineWidth: CGFloat = 0.0 var currentLineWidth: CGFloat = 0.0 var requiredWidth: CGFloat = 0.0 var stackedStartIndex: Int = -1 for i in 0 ..< entryCount { let e = entries[i] let drawingForm = e.form != .none let label = e.label calculatedLabelBreakPoints[i] = false if stackedStartIndex == -1 { // we are not stacking, so required width is for this label only requiredWidth = 0.0 } else { // add the spacing appropriate for stacked labels/forms requiredWidth += stackSpace } // grouped forms have null labels if label != nil { calculatedLabelSizes[i] = (label! as NSString).size(withAttributes: labelAttrs) requiredWidth += drawingForm ? formToTextSpace + formSize : 0.0 requiredWidth += calculatedLabelSizes[i].width } else { calculatedLabelSizes[i] = CGSize() requiredWidth += drawingForm ? formSize : 0.0 if stackedStartIndex == -1 { // mark this index as we might want to break here later stackedStartIndex = i } } if label != nil || i == entryCount - 1 { let requiredSpacing = currentLineWidth == 0.0 ? 0.0 : xEntrySpace if (!wordWrapEnabled || // No word wrapping, it must fit. currentLineWidth == 0.0 || // The line is empty, it must fit. (contentWidth - currentLineWidth >= requiredSpacing + requiredWidth)) // It simply fits { // Expand current line currentLineWidth += requiredSpacing + requiredWidth } else { // It doesn't fit, we need to wrap a line // Add current line size to array calculatedLineSizes.append(CGSize(width: currentLineWidth, height: labelLineHeight)) maxLineWidth = max(maxLineWidth, currentLineWidth) // Start a new line calculatedLabelBreakPoints[stackedStartIndex > -1 ? stackedStartIndex : i] = true currentLineWidth = requiredWidth } if i == entryCount - 1 { // Add last line size to array calculatedLineSizes.append(CGSize(width: currentLineWidth, height: labelLineHeight)) maxLineWidth = max(maxLineWidth, currentLineWidth) } } stackedStartIndex = label != nil ? -1 : stackedStartIndex } neededWidth = maxLineWidth neededHeight = labelLineHeight * CGFloat(calculatedLineSizes.count) + yEntrySpace * CGFloat(calculatedLineSizes.count == 0 ? 0 : (calculatedLineSizes.count - 1)) } neededWidth += xOffset neededHeight += yOffset } /// MARK: - Custom legend /// Sets a custom legend's entries array. /// * A nil label will start a group. /// This will disable the feature that automatically calculates the legend entries from the datasets. /// Call `resetCustom(...)` to re-enable automatic calculation (and then `notifyDataSetChanged()` is needed). @objc open func setCustom(entries: [LegendEntry]) { self.entries = entries _isLegendCustom = true } /// Calling this will disable the custom legend entries (set by `setLegend(...)`). Instead, the entries will again be calculated automatically (after `notifyDataSetChanged()` is called). @objc open func resetCustom() { _isLegendCustom = false } /// **default**: false (automatic legend) /// - returns: `true` if a custom legend entries has been set @objc open var isLegendCustom: Bool { return _isLegendCustom } } ================================================ FILE: Pods/Charts/Source/Charts/Components/LegendEntry.swift ================================================ // // LegendEntry.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartLegendEntry) open class LegendEntry: NSObject { public override init() { super.init() } /// - parameter label: The legend entry text. /// A `nil` label will start a group. /// - parameter form: The form to draw for this entry. /// - parameter formSize: Set to NaN to use the legend's default. /// - parameter formLineWidth: Set to NaN to use the legend's default. /// - parameter formLineDashPhase: Line dash configuration. /// - parameter formLineDashLengths: Line dash configurationas NaN to use the legend's default. /// - parameter formColor: The color for drawing the form. @objc public init(label: String?, form: Legend.Form, formSize: CGFloat, formLineWidth: CGFloat, formLineDashPhase: CGFloat, formLineDashLengths: [CGFloat]?, formColor: NSUIColor?) { self.label = label self.form = form self.formSize = formSize self.formLineWidth = formLineWidth self.formLineDashPhase = formLineDashPhase self.formLineDashLengths = formLineDashLengths self.formColor = formColor } /// The legend entry text. /// A `nil` label will start a group. @objc open var label: String? /// The form to draw for this entry. /// /// `None` will avoid drawing a form, and any related space. /// `Empty` will avoid drawing a form, but keep its space. /// `Default` will use the Legend's default. @objc open var form: Legend.Form = .default /// Form size will be considered except for when .None is used /// /// Set as NaN to use the legend's default @objc open var formSize: CGFloat = CGFloat.nan /// Line width used for shapes that consist of lines. /// /// Set to NaN to use the legend's default. @objc open var formLineWidth: CGFloat = CGFloat.nan /// Line dash configuration for shapes that consist of lines. /// /// This is how much (in pixels) into the dash pattern are we starting from. /// /// Set to NaN to use the legend's default. @objc open var formLineDashPhase: CGFloat = 0.0 /// Line dash configuration for shapes that consist of lines. /// /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] /// /// Set to nil to use the legend's default. @objc open var formLineDashLengths: [CGFloat]? /// The color for drawing the form @objc open var formColor: NSUIColor? } ================================================ FILE: Pods/Charts/Source/Charts/Components/MarkerImage.swift ================================================ // // ChartMarkerImage.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartMarkerImage) open class MarkerImage: NSObject, IMarker { /// The marker image to render @objc open var image: NSUIImage? open var offset: CGPoint = CGPoint() @objc open weak var chartView: ChartViewBase? /// As long as size is 0.0/0.0 - it will default to the image's size @objc open var size: CGSize = CGSize() public override init() { super.init() } open func offsetForDrawing(atPoint point: CGPoint) -> CGPoint { var offset = self.offset let chart = self.chartView var size = self.size if size.width == 0.0 && image != nil { size.width = image?.size.width ?? 0.0 } if size.height == 0.0 && image != nil { size.height = image?.size.height ?? 0.0 } let width = size.width let height = size.height if point.x + offset.x < 0.0 { offset.x = -point.x } else if chart != nil && point.x + width + offset.x > chart!.bounds.size.width { offset.x = chart!.bounds.size.width - point.x - width } if point.y + offset.y < 0 { offset.y = -point.y } else if chart != nil && point.y + height + offset.y > chart!.bounds.size.height { offset.y = chart!.bounds.size.height - point.y - height } return offset } open func refreshContent(entry: ChartDataEntry, highlight: Highlight) { // Do nothing here... } open func draw(context: CGContext, point: CGPoint) { guard let image = image else { return } let offset = offsetForDrawing(atPoint: point) var size = self.size if size.width == 0.0 { size.width = image.size.width } if size.height == 0.0 { size.height = image.size.height } let rect = CGRect( x: point.x + offset.x, y: point.y + offset.y, width: size.width, height: size.height) NSUIGraphicsPushContext(context) image.draw(in: rect) NSUIGraphicsPopContext() } } ================================================ FILE: Pods/Charts/Source/Charts/Components/MarkerView.swift ================================================ // // ChartMarkerView.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartMarkerView) open class MarkerView: NSUIView, IMarker { open var offset: CGPoint = CGPoint() @objc open weak var chartView: ChartViewBase? open func offsetForDrawing(atPoint point: CGPoint) -> CGPoint { guard let chart = chartView else { return self.offset } var offset = self.offset let width = self.bounds.size.width let height = self.bounds.size.height if point.x + offset.x < 0.0 { offset.x = -point.x } else if point.x + width + offset.x > chart.bounds.size.width { offset.x = chart.bounds.size.width - point.x - width } if point.y + offset.y < 0 { offset.y = -point.y } else if point.y + height + offset.y > chart.bounds.size.height { offset.y = chart.bounds.size.height - point.y - height } return offset } open func refreshContent(entry: ChartDataEntry, highlight: Highlight) { // Do nothing here... } open func draw(context: CGContext, point: CGPoint) { let offset = self.offsetForDrawing(atPoint: point) context.saveGState() context.translateBy(x: point.x + offset.x, y: point.y + offset.y) NSUIGraphicsPushContext(context) self.nsuiLayer?.render(in: context) NSUIGraphicsPopContext() context.restoreGState() } @objc open class func viewFromXib(in bundle: Bundle = .main) -> MarkerView? { #if !os(OSX) return bundle.loadNibNamed( String(describing: self), owner: nil, options: nil)?[0] as? MarkerView #else var loadedObjects = NSArray() let loadedObjectsPointer = AutoreleasingUnsafeMutablePointer(&loadedObjects) if bundle.loadNibNamed( NSNib.Name(String(describing: self)), owner: nil, topLevelObjects: loadedObjectsPointer) { return loadedObjects[0] as? MarkerView } return nil #endif } } ================================================ FILE: Pods/Charts/Source/Charts/Components/XAxis.swift ================================================ // // XAxis.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartXAxis) open class XAxis: AxisBase { @objc(XAxisLabelPosition) public enum LabelPosition: Int { case top case bottom case bothSided case topInside case bottomInside } /// width of the x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers @objc open var labelWidth = CGFloat(1.0) /// height of the x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers @objc open var labelHeight = CGFloat(1.0) /// width of the (rotated) x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers @objc open var labelRotatedWidth = CGFloat(1.0) /// height of the (rotated) x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers @objc open var labelRotatedHeight = CGFloat(1.0) /// This is the angle for drawing the X axis labels (in degrees) @objc open var labelRotationAngle = CGFloat(0.0) /// if set to true, the chart will avoid that the first and last label entry in the chart "clip" off the edge of the chart @objc open var avoidFirstLastClippingEnabled = false /// the position of the x-labels relative to the chart @objc open var labelPosition = LabelPosition.top /// if set to true, word wrapping the labels will be enabled. /// word wrapping is done using `(value width * labelRotatedWidth)` /// /// - note: currently supports all charts except pie/radar/horizontal-bar* @objc open var wordWrapEnabled = false /// - returns: `true` if word wrapping the labels is enabled @objc open var isWordWrapEnabled: Bool { return wordWrapEnabled } /// the width for wrapping the labels, as percentage out of one value width. /// used only when isWordWrapEnabled = true. /// /// **default**: 1.0 @objc open var wordWrapWidthPercent: CGFloat = 1.0 public override init() { super.init() self.yOffset = 4.0 } @objc open var isAvoidFirstLastClippingEnabled: Bool { return avoidFirstLastClippingEnabled } } ================================================ FILE: Pods/Charts/Source/Charts/Components/YAxis.swift ================================================ // // YAxis.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// Class representing the y-axis labels settings and its entries. /// Be aware that not all features the YLabels class provides are suitable for the RadarChart. /// Customizations that affect the value range of the axis need to be applied before setting data for the chart. @objc(ChartYAxis) open class YAxis: AxisBase { @objc(YAxisLabelPosition) public enum LabelPosition: Int { case outsideChart case insideChart } /// Enum that specifies the axis a DataSet should be plotted against, either Left or Right. @objc public enum AxisDependency: Int { case left case right } /// indicates if the bottom y-label entry is drawn or not @objc open var drawBottomYLabelEntryEnabled = true /// indicates if the top y-label entry is drawn or not @objc open var drawTopYLabelEntryEnabled = true /// flag that indicates if the axis is inverted or not @objc open var inverted = false /// flag that indicates if the zero-line should be drawn regardless of other grid lines @objc open var drawZeroLineEnabled = false /// Color of the zero line @objc open var zeroLineColor: NSUIColor? = NSUIColor.gray /// Width of the zero line @objc open var zeroLineWidth: CGFloat = 1.0 /// This is how much (in pixels) into the dash pattern are we starting from. @objc open var zeroLineDashPhase = CGFloat(0.0) /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] @objc open var zeroLineDashLengths: [CGFloat]? /// axis space from the largest value to the top in percent of the total axis range @objc open var spaceTop = CGFloat(0.1) /// axis space from the smallest value to the bottom in percent of the total axis range @objc open var spaceBottom = CGFloat(0.1) /// the position of the y-labels relative to the chart @objc open var labelPosition = LabelPosition.outsideChart /// the side this axis object represents private var _axisDependency = AxisDependency.left /// the minimum width that the axis should take /// /// **default**: 0.0 @objc open var minWidth = CGFloat(0) /// the maximum width that the axis can take. /// use Infinity for disabling the maximum. /// /// **default**: CGFloat.infinity @objc open var maxWidth = CGFloat(CGFloat.infinity) public override init() { super.init() self.yOffset = 0.0 } @objc public init(position: AxisDependency) { super.init() _axisDependency = position self.yOffset = 0.0 } @objc open var axisDependency: AxisDependency { return _axisDependency } @objc open func requiredSize() -> CGSize { let label = getLongestLabel() as NSString var size = label.size(withAttributes: [NSAttributedString.Key.font: labelFont]) size.width += xOffset * 2.0 size.height += yOffset * 2.0 size.width = max(minWidth, min(size.width, maxWidth > 0.0 ? maxWidth : size.width)) return size } @objc open func getRequiredHeightSpace() -> CGFloat { return requiredSize().height } /// - returns: `true` if this axis needs horizontal offset, `false` ifno offset is needed. @objc open var needsOffset: Bool { if isEnabled && isDrawLabelsEnabled && labelPosition == .outsideChart { return true } else { return false } } @objc open var isInverted: Bool { return inverted } open override func calculate(min dataMin: Double, max dataMax: Double) { // if custom, use value as is, else use data value var min = _customAxisMin ? _axisMinimum : dataMin var max = _customAxisMax ? _axisMaximum : dataMax // temporary range (before calculations) let range = abs(max - min) // in case all values are equal if range == 0.0 { max = max + 1.0 min = min - 1.0 } // bottom-space only effects non-custom min if !_customAxisMin { let bottomSpace = range * Double(spaceBottom) _axisMinimum = (min - bottomSpace) } // top-space only effects non-custom max if !_customAxisMax { let topSpace = range * Double(spaceTop) _axisMaximum = (max + topSpace) } // calc actual range axisRange = abs(_axisMaximum - _axisMinimum) } @objc open var isDrawBottomYLabelEntryEnabled: Bool { return drawBottomYLabelEntryEnabled } @objc open var isDrawTopYLabelEntryEnabled: Bool { return drawTopYLabelEntryEnabled } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/ChartBaseDataSet.swift ================================================ // // BaseDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ChartBaseDataSet: NSObject, IChartDataSet { public required override init() { super.init() // default color colors.append(NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)) valueColors.append(NSUIColor.black) } @objc public init(label: String?) { super.init() // default color colors.append(NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)) valueColors.append(NSUIColor.black) self.label = label } // MARK: - Data functions and accessors /// Use this method to tell the data set that the underlying data has changed open func notifyDataSetChanged() { calcMinMax() } open func calcMinMax() { fatalError("calcMinMax is not implemented in ChartBaseDataSet") } open func calcMinMaxY(fromX: Double, toX: Double) { fatalError("calcMinMaxY(fromX:, toX:) is not implemented in ChartBaseDataSet") } open var yMin: Double { fatalError("yMin is not implemented in ChartBaseDataSet") } open var yMax: Double { fatalError("yMax is not implemented in ChartBaseDataSet") } open var xMin: Double { fatalError("xMin is not implemented in ChartBaseDataSet") } open var xMax: Double { fatalError("xMax is not implemented in ChartBaseDataSet") } open var entryCount: Int { fatalError("entryCount is not implemented in ChartBaseDataSet") } open func entryForIndex(_ i: Int) -> ChartDataEntry? { fatalError("entryForIndex is not implemented in ChartBaseDataSet") } open func entryForXValue( _ x: Double, closestToY y: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? { fatalError("entryForXValue(x, closestToY, rounding) is not implemented in ChartBaseDataSet") } open func entryForXValue( _ x: Double, closestToY y: Double) -> ChartDataEntry? { fatalError("entryForXValue(x, closestToY) is not implemented in ChartBaseDataSet") } open func entriesForXValue(_ x: Double) -> [ChartDataEntry] { fatalError("entriesForXValue is not implemented in ChartBaseDataSet") } open func entryIndex( x xValue: Double, closestToY y: Double, rounding: ChartDataSetRounding) -> Int { fatalError("entryIndex(x, closestToY, rounding) is not implemented in ChartBaseDataSet") } open func entryIndex(entry e: ChartDataEntry) -> Int { fatalError("entryIndex(entry) is not implemented in ChartBaseDataSet") } open func addEntry(_ e: ChartDataEntry) -> Bool { fatalError("addEntry is not implemented in ChartBaseDataSet") } open func addEntryOrdered(_ e: ChartDataEntry) -> Bool { fatalError("addEntryOrdered is not implemented in ChartBaseDataSet") } @discardableResult open func removeEntry(_ entry: ChartDataEntry) -> Bool { fatalError("removeEntry is not implemented in ChartBaseDataSet") } @discardableResult open func removeEntry(index: Int) -> Bool { if let entry = entryForIndex(index) { return removeEntry(entry) } return false } @discardableResult open func removeEntry(x: Double) -> Bool { if let entry = entryForXValue(x, closestToY: Double.nan) { return removeEntry(entry) } return false } @discardableResult open func removeFirst() -> Bool { if entryCount > 0 { if let entry = entryForIndex(0) { return removeEntry(entry) } } return false } @discardableResult open func removeLast() -> Bool { if entryCount > 0 { if let entry = entryForIndex(entryCount - 1) { return removeEntry(entry) } } return false } open func contains(_ e: ChartDataEntry) -> Bool { fatalError("removeEntry is not implemented in ChartBaseDataSet") } open func clear() { fatalError("clear is not implemented in ChartBaseDataSet") } // MARK: - Styling functions and accessors /// All the colors that are used for this DataSet. /// Colors are reused as soon as the number of Entries the DataSet represents is higher than the size of the colors array. open var colors = [NSUIColor]() /// List representing all colors that are used for drawing the actual values for this DataSet open var valueColors = [NSUIColor]() /// The label string that describes the DataSet. open var label: String? = "DataSet" /// The axis this DataSet should be plotted against. open var axisDependency = YAxis.AxisDependency.left /// - returns: The color at the given index of the DataSet's color array. /// This prevents out-of-bounds by performing a modulus on the color index, so colours will repeat themselves. open func color(atIndex index: Int) -> NSUIColor { var index = index if index < 0 { index = 0 } return colors[index % colors.count] } /// Resets all colors of this DataSet and recreates the colors array. open func resetColors() { colors.removeAll(keepingCapacity: false) } /// Adds a new color to the colors array of the DataSet. /// - parameter color: the color to add open func addColor(_ color: NSUIColor) { colors.append(color) } /// Sets the one and **only** color that should be used for this DataSet. /// Internally, this recreates the colors array and adds the specified color. /// - parameter color: the color to set open func setColor(_ color: NSUIColor) { colors.removeAll(keepingCapacity: false) colors.append(color) } /// Sets colors to a single color a specific alpha value. /// - parameter color: the color to set /// - parameter alpha: alpha to apply to the set `color` @objc open func setColor(_ color: NSUIColor, alpha: CGFloat) { setColor(color.withAlphaComponent(alpha)) } /// Sets colors with a specific alpha value. /// - parameter colors: the colors to set /// - parameter alpha: alpha to apply to the set `colors` @objc open func setColors(_ colors: [NSUIColor], alpha: CGFloat) { var colorsWithAlpha = colors for i in 0 ..< colorsWithAlpha.count { colorsWithAlpha[i] = colorsWithAlpha[i] .withAlphaComponent(alpha) } self.colors = colorsWithAlpha } /// Sets colors with a specific alpha value. /// - parameter colors: the colors to set /// - parameter alpha: alpha to apply to the set `colors` open func setColors(_ colors: NSUIColor...) { self.colors = colors } /// if true, value highlighting is enabled open var highlightEnabled = true /// - returns: `true` if value highlighting is enabled for this dataset open var isHighlightEnabled: Bool { return highlightEnabled } /// Custom formatter that is used instead of the auto-formatter if set internal var _valueFormatter: IValueFormatter? /// Custom formatter that is used instead of the auto-formatter if set open var valueFormatter: IValueFormatter? { get { if needsFormatter { return ChartUtils.defaultValueFormatter() } return _valueFormatter } set { if newValue == nil { return } _valueFormatter = newValue } } open var needsFormatter: Bool { return _valueFormatter == nil } /// Sets/get a single color for value text. /// Setting the color clears the colors array and adds a single color. /// Getting will return the first color in the array. open var valueTextColor: NSUIColor { get { return valueColors[0] } set { valueColors.removeAll(keepingCapacity: false) valueColors.append(newValue) } } /// - returns: The color at the specified index that is used for drawing the values inside the chart. Uses modulus internally. open func valueTextColorAt(_ index: Int) -> NSUIColor { var index = index if index < 0 { index = 0 } return valueColors[index % valueColors.count] } /// the font for the value-text labels open var valueFont: NSUIFont = NSUIFont.systemFont(ofSize: 7.0) /// The form to draw for this dataset in the legend. open var form = Legend.Form.default /// The form size to draw for this dataset in the legend. /// /// Return `NaN` to use the default legend form size. open var formSize: CGFloat = CGFloat.nan /// The line width for drawing the form of this dataset in the legend /// /// Return `NaN` to use the default legend form line width. open var formLineWidth: CGFloat = CGFloat.nan /// Line dash configuration for legend shapes that consist of lines. /// /// This is how much (in pixels) into the dash pattern are we starting from. open var formLineDashPhase: CGFloat = 0.0 /// Line dash configuration for legend shapes that consist of lines. /// /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] open var formLineDashLengths: [CGFloat]? = nil /// Set this to true to draw y-values on the chart. /// /// - note: For bar and line charts: if `maxVisibleCount` is reached, no values will be drawn even if this is enabled. open var drawValuesEnabled = true /// - returns: `true` if y-value drawing is enabled, `false` ifnot open var isDrawValuesEnabled: Bool { return drawValuesEnabled } /// Set this to true to draw y-icons on the chart. /// /// - note: For bar and line charts: if `maxVisibleCount` is reached, no icons will be drawn even if this is enabled. open var drawIconsEnabled = true /// Returns true if y-icon drawing is enabled, false if not open var isDrawIconsEnabled: Bool { return drawIconsEnabled } /// Offset of icons drawn on the chart. /// /// For all charts except Pie and Radar it will be ordinary (x offset, y offset). /// /// For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint. open var iconsOffset = CGPoint(x: 0, y: 0) /// Set the visibility of this DataSet. If not visible, the DataSet will not be drawn to the chart upon refreshing it. open var visible = true /// - returns: `true` if this DataSet is visible inside the chart, or `false` ifit is currently hidden. open var isVisible: Bool { return visible } // MARK: - NSObject open override var description: String { return String(format: "%@, label: %@, %i entries", arguments: [NSStringFromClass(type(of: self)), self.label ?? "", self.entryCount]) } open override var debugDescription: String { var desc = description + ":" for i in 0 ..< self.entryCount { desc += "\n" + (self.entryForIndex(i)?.description ?? "") } return desc } // MARK: - NSCopying @objc open func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = type(of: self).init() copy.colors = colors copy.valueColors = valueColors copy.label = label return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartData.swift ================================================ // // BarChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BarChartData: BarLineScatterCandleBubbleChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } /// The width of the bars on the x-axis, in values (not pixels) /// /// **default**: 0.85 @objc open var barWidth = Double(0.85) /// Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries. /// Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified by the parameters. /// Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method. /// /// - parameter the starting point on the x-axis where the grouping should begin /// - parameter groupSpace: The space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f /// - parameter barSpace: The space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f @objc open func groupBars(fromX: Double, groupSpace: Double, barSpace: Double) { let setCount = _dataSets.count if setCount <= 1 { print("BarData needs to hold at least 2 BarDataSets to allow grouping.", terminator: "\n") return } let max = maxEntryCountSet let maxEntryCount = max?.entryCount ?? 0 let groupSpaceWidthHalf = groupSpace / 2.0 let barSpaceHalf = barSpace / 2.0 let barWidthHalf = self.barWidth / 2.0 var fromX = fromX let interval = groupWidth(groupSpace: groupSpace, barSpace: barSpace) for i in stride(from: 0, to: maxEntryCount, by: 1) { let start = fromX fromX += groupSpaceWidthHalf (_dataSets as? [IBarChartDataSet])?.forEach { set in fromX += barSpaceHalf fromX += barWidthHalf if i < set.entryCount { if let entry = set.entryForIndex(i) { entry.x = fromX } } fromX += barWidthHalf fromX += barSpaceHalf } fromX += groupSpaceWidthHalf let end = fromX let innerInterval = end - start let diff = interval - innerInterval // correct rounding errors if diff > 0 || diff < 0 { fromX += diff } } notifyDataChanged() } /// In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis. /// /// - parameter groupSpace: /// - parameter barSpace: @objc open func groupWidth(groupSpace: Double, barSpace: Double) -> Double { return Double(_dataSets.count) * (self.barWidth + barSpace) + groupSpace } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartDataEntry.swift ================================================ // // BarChartDataEntry.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class BarChartDataEntry: ChartDataEntry { /// the values the stacked barchart holds private var _yVals: [Double]? /// the ranges for the individual stack values - automatically calculated private var _ranges: [Range]? /// the sum of all negative values this entry (if stacked) contains private var _negativeSum: Double = 0.0 /// the sum of all positive values this entry (if stacked) contains private var _positiveSum: Double = 0.0 public required init() { super.init() } /// Constructor for normal bars (not stacked). public override init(x: Double, y: Double) { super.init(x: x, y: y) } /// Constructor for normal bars (not stacked). public override init(x: Double, y: Double, data: AnyObject?) { super.init(x: x, y: y, data: data) } /// Constructor for normal bars (not stacked). public override init(x: Double, y: Double, icon: NSUIImage?) { super.init(x: x, y: y, icon: icon) } /// Constructor for normal bars (not stacked). public override init(x: Double, y: Double, icon: NSUIImage?, data: AnyObject?) { super.init(x: x, y: y, icon: icon, data: data) } /// Constructor for stacked bar entries. @objc public init(x: Double, yValues: [Double]) { super.init(x: x, y: BarChartDataEntry.calcSum(values: yValues)) self._yVals = yValues calcPosNegSum() calcRanges() } /// Constructor for stacked bar entries. One data object for whole stack @objc public init(x: Double, yValues: [Double], data: AnyObject?) { super.init(x: x, y: BarChartDataEntry.calcSum(values: yValues), data: data) self._yVals = yValues calcPosNegSum() calcRanges() } /// Constructor for stacked bar entries. One data object for whole stack @objc public init(x: Double, yValues: [Double], icon: NSUIImage?, data: AnyObject?) { super.init(x: x, y: BarChartDataEntry.calcSum(values: yValues), icon: icon, data: data) self._yVals = yValues calcPosNegSum() calcRanges() } /// Constructor for stacked bar entries. One data object for whole stack @objc public init(x: Double, yValues: [Double], icon: NSUIImage?) { super.init(x: x, y: BarChartDataEntry.calcSum(values: yValues), icon: icon) self._yVals = yValues calcPosNegSum() calcRanges() } @objc open func sumBelow(stackIndex :Int) -> Double { guard let yVals = _yVals else { return 0 } var remainder: Double = 0.0 var index = yVals.count - 1 while (index > stackIndex && index >= 0) { remainder += yVals[index] index -= 1 } return remainder } /// - returns: The sum of all negative values this entry (if stacked) contains. (this is a positive number) @objc open var negativeSum: Double { return _negativeSum } /// - returns: The sum of all positive values this entry (if stacked) contains. @objc open var positiveSum: Double { return _positiveSum } @objc open func calcPosNegSum() { guard let _yVals = _yVals else { _positiveSum = 0.0 _negativeSum = 0.0 return } var sumNeg: Double = 0.0 var sumPos: Double = 0.0 for f in _yVals { if f < 0.0 { sumNeg += -f } else { sumPos += f } } _negativeSum = sumNeg _positiveSum = sumPos } /// Splits up the stack-values of the given bar-entry into Range objects. /// - parameter entry: /// - returns: @objc open func calcRanges() { let values = yValues if values?.isEmpty != false { return } if _ranges == nil { _ranges = [Range]() } else { _ranges?.removeAll() } _ranges?.reserveCapacity(values!.count) var negRemain = -negativeSum var posRemain: Double = 0.0 for i in 0 ..< values!.count { let value = values![i] if value < 0 { _ranges?.append(Range(from: negRemain, to: negRemain - value)) negRemain -= value } else { _ranges?.append(Range(from: posRemain, to: posRemain + value)) posRemain += value } } } // MARK: Accessors /// the values the stacked barchart holds @objc open var isStacked: Bool { return _yVals != nil } /// the values the stacked barchart holds @objc open var yValues: [Double]? { get { return self._yVals } set { self.y = BarChartDataEntry.calcSum(values: newValue) self._yVals = newValue calcPosNegSum() calcRanges() } } /// - returns: The ranges of the individual stack-entries. Will return null if this entry is not stacked. @objc open var ranges: [Range]? { return _ranges } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! BarChartDataEntry copy._yVals = _yVals copy.y = y copy._negativeSum = _negativeSum return copy } /// Calculates the sum across all values of the given stack. /// /// - parameter vals: /// - returns: private static func calcSum(values: [Double]?) -> Double { guard let values = values else { return 0.0 } var sum = 0.0 for f in values { sum += f } return sum } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BarChartDataSet.swift ================================================ // // BarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartDataSet { private func initialize() { self.highlightColor = NSUIColor.black self.calcStackSize(entries: values as! [BarChartDataEntry]) self.calcEntryCountIncludingStacks(entries: values as! [BarChartDataEntry]) } public required init() { super.init() initialize() } public override init(values: [ChartDataEntry]?, label: String?) { super.init(values: values, label: label) initialize() } // MARK: - Data functions and accessors /// the maximum number of bars that are stacked upon each other, this value /// is calculated from the Entries that are added to the DataSet private var _stackSize = 1 /// the overall entry count, including counting each stack-value individually private var _entryCountStacks = 0 /// Calculates the total number of entries this DataSet represents, including /// stacks. All values belonging to a stack are calculated separately. private func calcEntryCountIncludingStacks(entries: [BarChartDataEntry]) { _entryCountStacks = 0 for i in 0 ..< entries.count { if let vals = entries[i].yValues { _entryCountStacks += vals.count } else { _entryCountStacks += 1 } } } /// calculates the maximum stacksize that occurs in the Entries array of this DataSet private func calcStackSize(entries: [BarChartDataEntry]) { for i in 0 ..< entries.count { if let vals = entries[i].yValues { if vals.count > _stackSize { _stackSize = vals.count } } } } open override func calcMinMax(entry e: ChartDataEntry) { guard let e = e as? BarChartDataEntry else { return } if !e.y.isNaN { if e.yValues == nil { if e.y < _yMin { _yMin = e.y } if e.y > _yMax { _yMax = e.y } } else { if -e.negativeSum < _yMin { _yMin = -e.negativeSum } if e.positiveSum > _yMax { _yMax = e.positiveSum } } calcMinMaxX(entry: e) } } /// - returns: The maximum number of bars that can be stacked upon another in this DataSet. open var stackSize: Int { return _stackSize } /// - returns: `true` if this DataSet is stacked (stacksize > 1) or not. open var isStacked: Bool { return _stackSize > 1 ? true : false } /// - returns: The overall entry count, including counting each stack-value individually @objc open var entryCountStacks: Int { return _entryCountStacks } /// array of labels used to describe the different values of the stacked bars open var stackLabels: [String] = ["Stack"] // MARK: - Styling functions and accessors /// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value open var barShadowColor = NSUIColor(red: 215.0/255.0, green: 215.0/255.0, blue: 215.0/255.0, alpha: 1.0) /// the width used for drawing borders around the bars. If borderWidth == 0, no border will be drawn. open var barBorderWidth : CGFloat = 0.0 /// the color drawing borders around the bars. open var barBorderColor = NSUIColor.black /// the alpha value (transparency) that is used for drawing the highlight indicator bar. min = 0.0 (fully transparent), max = 1.0 (fully opaque) open var highlightAlpha = CGFloat(120.0 / 255.0) // MARK: - NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! BarChartDataSet copy._stackSize = _stackSize copy._entryCountStacks = _entryCountStacks copy.stackLabels = stackLabels copy.barShadowColor = barShadowColor copy.highlightAlpha = highlightAlpha return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift ================================================ // // BarLineScatterCandleBubbleChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class BarLineScatterCandleBubbleChartData: ChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartDataSet.swift ================================================ // // BarLineScatterCandleBubbleChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BarLineScatterCandleBubbleChartDataSet: ChartDataSet, IBarLineScatterCandleBubbleChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors open var highlightColor = NSUIColor(red: 255.0/255.0, green: 187.0/255.0, blue: 115.0/255.0, alpha: 1.0) open var highlightLineWidth = CGFloat(0.5) open var highlightLineDashPhase = CGFloat(0.0) open var highlightLineDashLengths: [CGFloat]? // MARK: - NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! BarLineScatterCandleBubbleChartDataSet copy.highlightColor = highlightColor copy.highlightLineWidth = highlightLineWidth copy.highlightLineDashPhase = highlightLineDashPhase copy.highlightLineDashLengths = highlightLineDashLengths return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartData.swift ================================================ // // BubbleChartData.swift // Charts // // Bubble chart implementation: // Copyright 2015 Pierre-Marc Airoldi // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BubbleChartData: BarLineScatterCandleBubbleChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } /// Sets the width of the circle that surrounds the bubble when highlighted for all DataSet objects this data object contains @objc open func setHighlightCircleWidth(_ width: CGFloat) { (_dataSets as? [IBubbleChartDataSet])?.forEach { $0.highlightCircleWidth = width } } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartDataEntry.swift ================================================ // // BubbleDataEntry.swift // Charts // // Bubble chart implementation: // Copyright 2015 Pierre-Marc Airoldi // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BubbleChartDataEntry: ChartDataEntry { /// The size of the bubble. @objc open var size = CGFloat(0.0) public required init() { super.init() } /// - parameter x: The index on the x-axis. /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. @objc public init(x: Double, y: Double, size: CGFloat) { super.init(x: x, y: y) self.size = size } /// - parameter x: The index on the x-axis. /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. /// - parameter data: Spot for additional data this Entry represents. @objc public init(x: Double, y: Double, size: CGFloat, data: AnyObject?) { super.init(x: x, y: y, data: data) self.size = size } /// - parameter x: The index on the x-axis. /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. /// - parameter icon: icon image @objc public init(x: Double, y: Double, size: CGFloat, icon: NSUIImage?) { super.init(x: x, y: y, icon: icon) self.size = size } /// - parameter x: The index on the x-axis. /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. /// - parameter icon: icon image /// - parameter data: Spot for additional data this Entry represents. @objc public init(x: Double, y: Double, size: CGFloat, icon: NSUIImage?, data: AnyObject?) { super.init(x: x, y: y, icon: icon, data: data) self.size = size } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! BubbleChartDataEntry copy.size = size return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/BubbleChartDataSet.swift ================================================ // // BubbleChartDataSet.swift // Charts // // Bubble chart implementation: // Copyright 2015 Pierre-Marc Airoldi // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class BubbleChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBubbleChartDataSet { // MARK: - Data functions and accessors internal var _maxSize = CGFloat(0.0) open var maxSize: CGFloat { return _maxSize } @objc open var normalizeSizeEnabled: Bool = true open var isNormalizeSizeEnabled: Bool { return normalizeSizeEnabled } open override func calcMinMax(entry e: ChartDataEntry) { guard let e = e as? BubbleChartDataEntry else { return } super.calcMinMax(entry: e) let size = e.size if size > _maxSize { _maxSize = size } } // MARK: - Styling functions and accessors /// Sets/gets the width of the circle that surrounds the bubble when highlighted open var highlightCircleWidth: CGFloat = 2.5 // MARK: - NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! BubbleChartDataSet copy._xMin = _xMin copy._xMax = _xMax copy._maxSize = _maxSize copy.highlightCircleWidth = highlightCircleWidth return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartData.swift ================================================ // // CandleChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class CandleChartData: BarLineScatterCandleBubbleChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartDataEntry.swift ================================================ // // CandleChartDataEntry.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class CandleChartDataEntry: ChartDataEntry { /// shadow-high value @objc open var high = Double(0.0) /// shadow-low value @objc open var low = Double(0.0) /// close value @objc open var close = Double(0.0) /// open value @objc open var open = Double(0.0) public required init() { super.init() } @objc public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double) { super.init(x: x, y: (shadowH + shadowL) / 2.0) self.high = shadowH self.low = shadowL self.open = open self.close = close } @objc public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double, data: AnyObject?) { super.init(x: x, y: (shadowH + shadowL) / 2.0, data: data) self.high = shadowH self.low = shadowL self.open = open self.close = close } @objc public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double, icon: NSUIImage?) { super.init(x: x, y: (shadowH + shadowL) / 2.0, icon: icon) self.high = shadowH self.low = shadowL self.open = open self.close = close } @objc public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double, icon: NSUIImage?, data: AnyObject?) { super.init(x: x, y: (shadowH + shadowL) / 2.0, icon: icon, data: data) self.high = shadowH self.low = shadowL self.open = open self.close = close } /// - returns: The overall range (difference) between shadow-high and shadow-low. @objc open var shadowRange: Double { return abs(high - low) } /// - returns: The body size (difference between open and close). @objc open var bodyRange: Double { return abs(open - close) } /// the center value of the candle. (Middle value between high and low) open override var y: Double { get { return super.y } set { super.y = (high + low) / 2.0 } } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! CandleChartDataEntry copy.high = high copy.low = low copy.open = open copy.close = close return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/CandleChartDataSet.swift ================================================ // // CandleChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class CandleChartDataSet: LineScatterCandleRadarChartDataSet, ICandleChartDataSet { public required init() { super.init() } public override init(values: [ChartDataEntry]?, label: String?) { super.init(values: values, label: label) } // MARK: - Data functions and accessors open override func calcMinMax(entry e: ChartDataEntry) { guard let e = e as? CandleChartDataEntry else { return } if e.low < _yMin { _yMin = e.low } if e.high > _yMax { _yMax = e.high } calcMinMaxX(entry: e) } open override func calcMinMaxY(entry e: ChartDataEntry) { guard let e = e as? CandleChartDataEntry else { return } if e.high < _yMin { _yMin = e.high } if e.high > _yMax { _yMax = e.high } if e.low < _yMin { _yMin = e.low } if e.low > _yMax { _yMax = e.low } } // MARK: - Styling functions and accessors /// the space between the candle entries /// /// **default**: 0.1 (10%) private var _barSpace = CGFloat(0.1) /// the space that is left out on the left and right side of each candle, /// **default**: 0.1 (10%), max 0.45, min 0.0 open var barSpace: CGFloat { set { if newValue < 0.0 { _barSpace = 0.0 } else if newValue > 0.45 { _barSpace = 0.45 } else { _barSpace = newValue } } get { return _barSpace } } /// should the candle bars show? /// when false, only "ticks" will show /// /// **default**: true open var showCandleBar: Bool = true /// the width of the candle-shadow-line in pixels. /// /// **default**: 1.5 open var shadowWidth = CGFloat(1.5) /// the color of the shadow line open var shadowColor: NSUIColor? /// use candle color for the shadow open var shadowColorSameAsCandle = false /// Is the shadow color same as the candle color? open var isShadowColorSameAsCandle: Bool { return shadowColorSameAsCandle } /// color for open == close open var neutralColor: NSUIColor? /// color for open > close open var increasingColor: NSUIColor? /// color for open < close open var decreasingColor: NSUIColor? /// Are increasing values drawn as filled? /// increasing candlesticks are traditionally hollow open var increasingFilled = false /// Are increasing values drawn as filled? open var isIncreasingFilled: Bool { return increasingFilled } /// Are decreasing values drawn as filled? /// descreasing candlesticks are traditionally filled open var decreasingFilled = true /// Are decreasing values drawn as filled? open var isDecreasingFilled: Bool { return decreasingFilled } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartData.swift ================================================ // // ChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class ChartData: NSObject { internal var _yMax: Double = -Double.greatestFiniteMagnitude internal var _yMin: Double = Double.greatestFiniteMagnitude internal var _xMax: Double = -Double.greatestFiniteMagnitude internal var _xMin: Double = Double.greatestFiniteMagnitude internal var _leftAxisMax: Double = -Double.greatestFiniteMagnitude internal var _leftAxisMin: Double = Double.greatestFiniteMagnitude internal var _rightAxisMax: Double = -Double.greatestFiniteMagnitude internal var _rightAxisMin: Double = Double.greatestFiniteMagnitude internal var _dataSets = [IChartDataSet]() public override init() { super.init() _dataSets = [IChartDataSet]() } @objc public init(dataSets: [IChartDataSet]?) { super.init() _dataSets = dataSets ?? [IChartDataSet]() self.initialize(dataSets: _dataSets) } @objc public convenience init(dataSet: IChartDataSet?) { self.init(dataSets: dataSet === nil ? nil : [dataSet!]) } internal func initialize(dataSets: [IChartDataSet]) { notifyDataChanged() } /// Call this method to let the ChartData know that the underlying data has changed. /// Calling this performs all necessary recalculations needed when the contained data has changed. @objc open func notifyDataChanged() { calcMinMax() } @objc open func calcMinMaxY(fromX: Double, toX: Double) { for set in _dataSets { set.calcMinMaxY(fromX: fromX, toX: toX) } // apply the new data calcMinMax() } /// calc minimum and maximum y value over all datasets @objc open func calcMinMax() { _yMax = -Double.greatestFiniteMagnitude _yMin = Double.greatestFiniteMagnitude _xMax = -Double.greatestFiniteMagnitude _xMin = Double.greatestFiniteMagnitude for set in _dataSets { calcMinMax(dataSet: set) } _leftAxisMax = -Double.greatestFiniteMagnitude _leftAxisMin = Double.greatestFiniteMagnitude _rightAxisMax = -Double.greatestFiniteMagnitude _rightAxisMin = Double.greatestFiniteMagnitude // left axis let firstLeft = getFirstLeft(dataSets: dataSets) if firstLeft !== nil { _leftAxisMax = firstLeft!.yMax _leftAxisMin = firstLeft!.yMin for dataSet in _dataSets { if dataSet.axisDependency == .left { if dataSet.yMin < _leftAxisMin { _leftAxisMin = dataSet.yMin } if dataSet.yMax > _leftAxisMax { _leftAxisMax = dataSet.yMax } } } } // right axis let firstRight = getFirstRight(dataSets: dataSets) if firstRight !== nil { _rightAxisMax = firstRight!.yMax _rightAxisMin = firstRight!.yMin for dataSet in _dataSets { if dataSet.axisDependency == .right { if dataSet.yMin < _rightAxisMin { _rightAxisMin = dataSet.yMin } if dataSet.yMax > _rightAxisMax { _rightAxisMax = dataSet.yMax } } } } } /// Adjusts the current minimum and maximum values based on the provided Entry object. @objc open func calcMinMax(entry e: ChartDataEntry, axis: YAxis.AxisDependency) { if _yMax < e.y { _yMax = e.y } if _yMin > e.y { _yMin = e.y } if _xMax < e.x { _xMax = e.x } if _xMin > e.x { _xMin = e.x } if axis == .left { if _leftAxisMax < e.y { _leftAxisMax = e.y } if _leftAxisMin > e.y { _leftAxisMin = e.y } } else { if _rightAxisMax < e.y { _rightAxisMax = e.y } if _rightAxisMin > e.y { _rightAxisMin = e.y } } } /// Adjusts the minimum and maximum values based on the given DataSet. @objc open func calcMinMax(dataSet d: IChartDataSet) { if _yMax < d.yMax { _yMax = d.yMax } if _yMin > d.yMin { _yMin = d.yMin } if _xMax < d.xMax { _xMax = d.xMax } if _xMin > d.xMin { _xMin = d.xMin } if d.axisDependency == .left { if _leftAxisMax < d.yMax { _leftAxisMax = d.yMax } if _leftAxisMin > d.yMin { _leftAxisMin = d.yMin } } else { if _rightAxisMax < d.yMax { _rightAxisMax = d.yMax } if _rightAxisMin > d.yMin { _rightAxisMin = d.yMin } } } /// - returns: The number of LineDataSets this object contains @objc open var dataSetCount: Int { return _dataSets.count } /// - returns: The smallest y-value the data object contains. @objc open var yMin: Double { return _yMin } @nonobjc open func getYMin() -> Double { return _yMin } @objc open func getYMin(axis: YAxis.AxisDependency) -> Double { if axis == .left { if _leftAxisMin == Double.greatestFiniteMagnitude { return _rightAxisMin } else { return _leftAxisMin } } else { if _rightAxisMin == Double.greatestFiniteMagnitude { return _leftAxisMin } else { return _rightAxisMin } } } /// - returns: The greatest y-value the data object contains. @objc open var yMax: Double { return _yMax } @nonobjc open func getYMax() -> Double { return _yMax } @objc open func getYMax(axis: YAxis.AxisDependency) -> Double { if axis == .left { if _leftAxisMax == -Double.greatestFiniteMagnitude { return _rightAxisMax } else { return _leftAxisMax } } else { if _rightAxisMax == -Double.greatestFiniteMagnitude { return _leftAxisMax } else { return _rightAxisMax } } } /// - returns: The minimum x-value the data object contains. @objc open var xMin: Double { return _xMin } /// - returns: The maximum x-value the data object contains. @objc open var xMax: Double { return _xMax } /// - returns: All DataSet objects this ChartData object holds. @objc open var dataSets: [IChartDataSet] { get { return _dataSets } set { _dataSets = newValue notifyDataChanged() } } /// Retrieve the index of a ChartDataSet with a specific label from the ChartData. Search can be case sensitive or not. /// /// **IMPORTANT: This method does calculations at runtime, do not over-use in performance critical situations.** /// /// - parameter dataSets: the DataSet array to search /// - parameter type: /// - parameter ignorecase: if true, the search is not case-sensitive /// - returns: The index of the DataSet Object with the given label. Sensitive or not. internal func getDataSetIndexByLabel(_ label: String, ignorecase: Bool) -> Int { if ignorecase { for i in 0 ..< dataSets.count { if dataSets[i].label == nil { continue } if (label.caseInsensitiveCompare(dataSets[i].label!) == ComparisonResult.orderedSame) { return i } } } else { for i in 0 ..< dataSets.count { if label == dataSets[i].label { return i } } } return -1 } /// - returns: The labels of all DataSets as a string array. internal func dataSetLabels() -> [String] { var types = [String]() for i in 0 ..< _dataSets.count { if dataSets[i].label == nil { continue } types[i] = _dataSets[i].label! } return types } /// Get the Entry for a corresponding highlight object /// /// - parameter highlight: /// - returns: The entry that is highlighted @objc open func entryForHighlight(_ highlight: Highlight) -> ChartDataEntry? { if highlight.dataSetIndex >= dataSets.count { return nil } else { return dataSets[highlight.dataSetIndex].entryForXValue(highlight.x, closestToY: highlight.y) } } /// **IMPORTANT: This method does calculations at runtime. Use with care in performance critical situations.** /// /// - parameter label: /// - parameter ignorecase: /// - returns: The DataSet Object with the given label. Sensitive or not. @objc open func getDataSetByLabel(_ label: String, ignorecase: Bool) -> IChartDataSet? { let index = getDataSetIndexByLabel(label, ignorecase: ignorecase) if index < 0 || index >= _dataSets.count { return nil } else { return _dataSets[index] } } @objc open func getDataSetByIndex(_ index: Int) -> IChartDataSet! { if index < 0 || index >= _dataSets.count { return nil } return _dataSets[index] } @objc open func addDataSet(_ dataSet: IChartDataSet!) { calcMinMax(dataSet: dataSet) _dataSets.append(dataSet) } /// Removes the given DataSet from this data object. /// Also recalculates all minimum and maximum values. /// /// - returns: `true` if a DataSet was removed, `false` ifno DataSet could be removed. @objc @discardableResult open func removeDataSet(_ dataSet: IChartDataSet!) -> Bool { if dataSet === nil { return false } for i in 0 ..< _dataSets.count { if _dataSets[i] === dataSet { return removeDataSetByIndex(i) } } return false } /// Removes the DataSet at the given index in the DataSet array from the data object. /// Also recalculates all minimum and maximum values. /// /// - returns: `true` if a DataSet was removed, `false` ifno DataSet could be removed. @objc @discardableResult open func removeDataSetByIndex(_ index: Int) -> Bool { if index >= _dataSets.count || index < 0 { return false } _dataSets.remove(at: index) calcMinMax() return true } /// Adds an Entry to the DataSet at the specified index. Entries are added to the end of the list. @objc open func addEntry(_ e: ChartDataEntry, dataSetIndex: Int) { if _dataSets.count > dataSetIndex && dataSetIndex >= 0 { let set = _dataSets[dataSetIndex] if !set.addEntry(e) { return } calcMinMax(entry: e, axis: set.axisDependency) } else { print("ChartData.addEntry() - Cannot add Entry because dataSetIndex too high or too low.", terminator: "\n") } } /// Removes the given Entry object from the DataSet at the specified index. @objc @discardableResult open func removeEntry(_ entry: ChartDataEntry, dataSetIndex: Int) -> Bool { // entry outofbounds if dataSetIndex >= _dataSets.count { return false } // remove the entry from the dataset let removed = _dataSets[dataSetIndex].removeEntry(entry) if removed { calcMinMax() } return removed } /// Removes the Entry object closest to the given xIndex from the ChartDataSet at the /// specified index. /// - returns: `true` if an entry was removed, `false` ifno Entry was found that meets the specified requirements. @objc @discardableResult open func removeEntry(xValue: Double, dataSetIndex: Int) -> Bool { if dataSetIndex >= _dataSets.count { return false } if let entry = _dataSets[dataSetIndex].entryForXValue(xValue, closestToY: Double.nan) { return removeEntry(entry, dataSetIndex: dataSetIndex) } return false } /// - returns: The DataSet that contains the provided Entry, or null, if no DataSet contains this entry. @objc open func getDataSetForEntry(_ e: ChartDataEntry!) -> IChartDataSet? { if e == nil { return nil } for i in 0 ..< _dataSets.count { let set = _dataSets[i] if e === set.entryForXValue(e.x, closestToY: e.y) { return set } } return nil } /// - returns: The index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist. @objc open func indexOfDataSet(_ dataSet: IChartDataSet) -> Int { for i in 0 ..< _dataSets.count { if _dataSets[i] === dataSet { return i } } return -1 } /// - returns: The first DataSet from the datasets-array that has it's dependency on the left axis. Returns null if no DataSet with left dependency could be found. @objc open func getFirstLeft(dataSets: [IChartDataSet]) -> IChartDataSet? { for dataSet in dataSets { if dataSet.axisDependency == .left { return dataSet } } return nil } /// - returns: The first DataSet from the datasets-array that has it's dependency on the right axis. Returns null if no DataSet with right dependency could be found. @objc open func getFirstRight(dataSets: [IChartDataSet]) -> IChartDataSet? { for dataSet in _dataSets { if dataSet.axisDependency == .right { return dataSet } } return nil } /// - returns: All colors used across all DataSet objects this object represents. @objc open func getColors() -> [NSUIColor]? { var clrcnt = 0 for i in 0 ..< _dataSets.count { clrcnt += _dataSets[i].colors.count } var colors = [NSUIColor]() for i in 0 ..< _dataSets.count { let clrs = _dataSets[i].colors for clr in clrs { colors.append(clr) } } return colors } /// Sets a custom IValueFormatter for all DataSets this data object contains. @objc open func setValueFormatter(_ formatter: IValueFormatter?) { guard let formatter = formatter else { return } for set in dataSets { set.valueFormatter = formatter } } /// Sets the color of the value-text (color in which the value-labels are drawn) for all DataSets this data object contains. @objc open func setValueTextColor(_ color: NSUIColor!) { for set in dataSets { set.valueTextColor = color ?? set.valueTextColor } } /// Sets the font for all value-labels for all DataSets this data object contains. @objc open func setValueFont(_ font: NSUIFont!) { for set in dataSets { set.valueFont = font ?? set.valueFont } } /// Enables / disables drawing values (value-text) for all DataSets this data object contains. @objc open func setDrawValues(_ enabled: Bool) { for set in dataSets { set.drawValuesEnabled = enabled } } /// Enables / disables highlighting values for all DataSets this data object contains. /// If set to true, this means that values can be highlighted programmatically or by touch gesture. @objc open var highlightEnabled: Bool { get { for set in dataSets { if !set.highlightEnabled { return false } } return true } set { for set in dataSets { set.highlightEnabled = newValue } } } /// if true, value highlightning is enabled @objc open var isHighlightEnabled: Bool { return highlightEnabled } /// Clears this data object from all DataSets and removes all Entries. /// Don't forget to invalidate the chart after this. @objc open func clearValues() { dataSets.removeAll(keepingCapacity: false) notifyDataChanged() } /// Checks if this data object contains the specified DataSet. /// - returns: `true` if so, `false` ifnot. @objc open func contains(dataSet: IChartDataSet) -> Bool { for set in dataSets { if set === dataSet { return true } } return false } /// - returns: The total entry count across all DataSet objects this data object contains. @objc open var entryCount: Int { var count = 0 for set in _dataSets { count += set.entryCount } return count } /// - returns: The DataSet object with the maximum number of entries or null if there are no DataSets. @objc open var maxEntryCountSet: IChartDataSet? { if _dataSets.count == 0 { return nil } var max = _dataSets[0] for set in _dataSets { if set.entryCount > max.entryCount { max = set } } return max } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataEntry.swift ================================================ // // ChartDataEntry.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class ChartDataEntry: ChartDataEntryBase { /// the x value @objc open var x = Double(0.0) public required init() { super.init() } /// An Entry represents one single entry in the chart. /// - parameter x: the x value /// - parameter y: the y value (the actual value of the entry) @objc public init(x: Double, y: Double) { super.init(y: y) self.x = x } /// An Entry represents one single entry in the chart. /// - parameter x: the x value /// - parameter y: the y value (the actual value of the entry) /// - parameter data: Space for additional data this Entry represents. @objc public init(x: Double, y: Double, data: AnyObject?) { super.init(y: y) self.x = x self.data = data } /// An Entry represents one single entry in the chart. /// - parameter x: the x value /// - parameter y: the y value (the actual value of the entry) /// - parameter icon: icon image @objc public init(x: Double, y: Double, icon: NSUIImage?) { super.init(y: y, icon: icon) self.x = x } /// An Entry represents one single entry in the chart. /// - parameter x: the x value /// - parameter y: the y value (the actual value of the entry) /// - parameter icon: icon image /// - parameter data: Space for additional data this Entry represents. @objc public init(x: Double, y: Double, icon: NSUIImage?, data: AnyObject?) { super.init(y: y, icon: icon, data: data) self.x = x } // MARK: NSObject open override var description: String { return "ChartDataEntry, x: \(x), y \(y)" } // MARK: NSCopying @objc open func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = type(of: self).init() copy.x = x copy.y = y copy.data = data return copy } } // MARK: Equatable extension ChartDataEntry/*: Equatable*/ { open override func isEqual(_ object: Any?) -> Bool { guard let object = object as? ChartDataEntry else { return false } if self === object { return true } return ((data == nil && object.data == nil) || (data?.isEqual(object.data) ?? false)) && y == object.y && x == object.x } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataEntryBase.swift ================================================ // // ChartDataEntryBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class ChartDataEntryBase: NSObject { /// the y value @objc open var y = Double(0.0) /// optional spot for additional data this Entry represents @objc open var data: AnyObject? /// optional icon image @objc open var icon: NSUIImage? public override required init() { super.init() } /// An Entry represents one single entry in the chart. /// - parameter y: the y value (the actual value of the entry) @objc public init(y: Double) { super.init() self.y = y } /// - parameter y: the y value (the actual value of the entry) /// - parameter data: Space for additional data this Entry represents. @objc public init(y: Double, data: AnyObject?) { super.init() self.y = y self.data = data } /// - parameter y: the y value (the actual value of the entry) /// - parameter icon: icon image @objc public init(y: Double, icon: NSUIImage?) { super.init() self.y = y self.icon = icon } /// - parameter y: the y value (the actual value of the entry) /// - parameter icon: icon image /// - parameter data: Space for additional data this Entry represents. @objc public init(y: Double, icon: NSUIImage?, data: AnyObject?) { super.init() self.y = y self.icon = icon self.data = data } // MARK: NSObject open override var description: String { return "ChartDataEntryBase, y \(y)" } } // MARK: Equatable extension ChartDataEntryBase/*: Equatable*/ { open override func isEqual(_ object: Any?) -> Bool { guard let object = object as? ChartDataEntryBase else { return false } if self === object { return true } return ((data == nil && object.data == nil) || (data?.isEqual(object.data) ?? false)) && y == object.y } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ChartDataSet.swift ================================================ // // ChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation /// Determines how to round DataSet index values for `ChartDataSet.entryIndex(x, rounding)` when an exact x-value is not found. @objc public enum ChartDataSetRounding: Int { case up = 0 case down = 1 case closest = 2 } /// The DataSet class represents one group or type of entries (Entry) in the Chart that belong together. /// It is designed to logically separate different groups of values inside the Chart (e.g. the values for a specific line in the LineChart, or the values of a specific group of bars in the BarChart). open class ChartDataSet: ChartBaseDataSet { public required init() { values = [] super.init() } public override init(label: String?) { values = [] super.init(label: label) } @objc public init(values: [ChartDataEntry]?, label: String?) { self.values = values ?? [] super.init(label: label) self.calcMinMax() } @objc public convenience init(values: [ChartDataEntry]?) { self.init(values: values, label: "DataSet") } // MARK: - Data functions and accessors /// * /// - note: Calls `notifyDataSetChanged()` after setting a new value. /// - returns: The array of y-values that this DataSet represents. /// the entries that this dataset represents / holds together @objc open var values: [ChartDataEntry] { didSet { if isIndirectValuesCall { isIndirectValuesCall = false return } notifyDataSetChanged() } } // TODO: Temporary fix for performance. Will be removed in 4.0 private var isIndirectValuesCall = false /// maximum y-value in the value array internal var _yMax: Double = -Double.greatestFiniteMagnitude /// minimum y-value in the value array internal var _yMin: Double = Double.greatestFiniteMagnitude /// maximum x-value in the value array internal var _xMax: Double = -Double.greatestFiniteMagnitude /// minimum x-value in the value array internal var _xMin: Double = Double.greatestFiniteMagnitude open override func calcMinMax() { _yMax = -Double.greatestFiniteMagnitude _yMin = Double.greatestFiniteMagnitude _xMax = -Double.greatestFiniteMagnitude _xMin = Double.greatestFiniteMagnitude guard !values.isEmpty else { return } values.forEach { calcMinMax(entry: $0) } } open override func calcMinMaxY(fromX: Double, toX: Double) { _yMax = -Double.greatestFiniteMagnitude _yMin = Double.greatestFiniteMagnitude guard !values.isEmpty else { return } let indexFrom = entryIndex(x: fromX, closestToY: Double.nan, rounding: .down) let indexTo = entryIndex(x: toX, closestToY: Double.nan, rounding: .up) guard !(indexTo < indexFrom) else { return } (indexFrom...indexTo).forEach { // only recalculate y calcMinMaxY(entry: values[$0]) } } @objc open func calcMinMaxX(entry e: ChartDataEntry) { if e.x < _xMin { _xMin = e.x } if e.x > _xMax { _xMax = e.x } } @objc open func calcMinMaxY(entry e: ChartDataEntry) { if e.y < _yMin { _yMin = e.y } if e.y > _yMax { _yMax = e.y } } /// Updates the min and max x and y value of this DataSet based on the given Entry. /// /// - parameter e: internal func calcMinMax(entry e: ChartDataEntry) { calcMinMaxX(entry: e) calcMinMaxY(entry: e) } /// - returns: The minimum y-value this DataSet holds open override var yMin: Double { return _yMin } /// - returns: The maximum y-value this DataSet holds open override var yMax: Double { return _yMax } /// - returns: The minimum x-value this DataSet holds open override var xMin: Double { return _xMin } /// - returns: The maximum x-value this DataSet holds open override var xMax: Double { return _xMax } /// - returns: The number of y-values this DataSet represents open override var entryCount: Int { return values.count } /// - returns: The entry object found at the given index (not x-value!) /// - throws: out of bounds /// if `i` is out of bounds, it may throw an out-of-bounds exception open override func entryForIndex(_ i: Int) -> ChartDataEntry? { guard i >= values.startIndex, i < values.endIndex else { return nil } return values[i] } /// - returns: The first Entry object found at the given x-value with binary search. /// If the no Entry at the specified x-value is found, this method returns the Entry at the closest x-value according to the rounding. /// nil if no Entry object at that x-value. /// - parameter xValue: the x-value /// - parameter closestToY: If there are multiple y-values for the specified x-value, /// - parameter rounding: determine whether to round up/down/closest if there is no Entry matching the provided x-value open override func entryForXValue( _ xValue: Double, closestToY yValue: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? { let index = entryIndex(x: xValue, closestToY: yValue, rounding: rounding) if index > -1 { return values[index] } return nil } /// - returns: The first Entry object found at the given x-value with binary search. /// If the no Entry at the specified x-value is found, this method returns the Entry at the closest x-value. /// nil if no Entry object at that x-value. /// - parameter xValue: the x-value /// - parameter closestToY: If there are multiple y-values for the specified x-value, open override func entryForXValue( _ xValue: Double, closestToY yValue: Double) -> ChartDataEntry? { return entryForXValue(xValue, closestToY: yValue, rounding: .closest) } /// - returns: All Entry objects found at the given xIndex with binary search. /// An empty array if no Entry object at that index. open override func entriesForXValue(_ xValue: Double) -> [ChartDataEntry] { var entries = [ChartDataEntry]() var low = values.startIndex var high = values.endIndex - 1 while low <= high { var m = (high + low) / 2 var entry = values[m] // if we have a match if xValue == entry.x { while m > 0 && values[m - 1].x == xValue { m -= 1 } high = values.endIndex // loop over all "equal" entries while m < high { entry = values[m] if entry.x == xValue { entries.append(entry) } else { break } m += 1 } break } else { if xValue > entry.x { low = m + 1 } else { high = m - 1 } } } return entries } /// - returns: The array-index of the specified entry. /// If the no Entry at the specified x-value is found, this method returns the index of the Entry at the closest x-value according to the rounding. /// /// - parameter xValue: x-value of the entry to search for /// - parameter closestToY: If there are multiple y-values for the specified x-value, /// - parameter rounding: Rounding method if exact value was not found open override func entryIndex( x xValue: Double, closestToY yValue: Double, rounding: ChartDataSetRounding) -> Int { var low = values.startIndex var high = values.endIndex - 1 var closest = high while low < high { let m = (low + high) / 2 let d1 = values[m].x - xValue let d2 = values[m + 1].x - xValue let ad1 = abs(d1), ad2 = abs(d2) if ad2 < ad1 { // [m + 1] is closer to xValue // Search in an higher place low = m + 1 } else if ad1 < ad2 { // [m] is closer to xValue // Search in a lower place high = m } else { // We have multiple sequential x-value with same distance if d1 >= 0.0 { // Search in a lower place high = m } else if d1 < 0.0 { // Search in an higher place low = m + 1 } } closest = high } if closest != -1 { let closestXValue = values[closest].x if rounding == .up { // If rounding up, and found x-value is lower than specified x, and we can go upper... if closestXValue < xValue && closest < values.endIndex - 1 { closest += 1 } } else if rounding == .down { // If rounding down, and found x-value is upper than specified x, and we can go lower... if closestXValue > xValue && closest > 0 { closest -= 1 } } // Search by closest to y-value if !yValue.isNaN { while closest > 0 && values[closest - 1].x == closestXValue { closest -= 1 } var closestYValue = values[closest].y var closestYIndex = closest while true { closest += 1 if closest >= values.endIndex { break } let value = values[closest] if value.x != closestXValue { break } if abs(value.y - yValue) < abs(closestYValue - yValue) { closestYValue = yValue closestYIndex = closest } } closest = closestYIndex } } return closest } /// - returns: The array-index of the specified entry /// /// - parameter e: the entry to search for open override func entryIndex(entry e: ChartDataEntry) -> Int { for i in 0 ..< values.count { if values[i] === e { return i } } return -1 } /// Adds an Entry to the DataSet dynamically. /// Entries are added to the end of the list. /// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum. /// - parameter e: the entry to add /// - returns: True open override func addEntry(_ e: ChartDataEntry) -> Bool { calcMinMax(entry: e) isIndirectValuesCall = true values.append(e) return true } /// Adds an Entry to the DataSet dynamically. /// Entries are added to their appropriate index respective to it's x-index. /// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum. /// - parameter e: the entry to add /// - returns: True open override func addEntryOrdered(_ e: ChartDataEntry) -> Bool { calcMinMax(entry: e) isIndirectValuesCall = true if values.count > 0 && values.last!.x > e.x { var closestIndex = entryIndex(x: e.x, closestToY: e.y, rounding: .up) while values[closestIndex].x < e.x { closestIndex += 1 } values.insert(e, at: closestIndex) } else { values.append(e) } return true } /// Removes an Entry from the DataSet dynamically. /// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum. /// - parameter entry: the entry to remove /// - returns: `true` if the entry was removed successfully, else if the entry does not exist open override func removeEntry(_ entry: ChartDataEntry) -> Bool { var removed = false isIndirectValuesCall = true for i in 0 ..< values.count { if values[i] === entry { values.remove(at: i) removed = true break } } notifyDataSetChanged() return removed } /// Removes the first Entry (at index 0) of this DataSet from the entries array. /// /// - returns: `true` if successful, `false` if not. open override func removeFirst() -> Bool { let entry: ChartDataEntry? = values.isEmpty ? nil : values.removeFirst() return entry != nil } /// Removes the last Entry (at index size-1) of this DataSet from the entries array. /// /// - returns: `true` if successful, `false` if not. open override func removeLast() -> Bool { let entry: ChartDataEntry? = values.isEmpty ? nil : values.removeLast() return entry != nil } /// Checks if this DataSet contains the specified Entry. /// - returns: `true` if contains the entry, `false` if not. open override func contains(_ e: ChartDataEntry) -> Bool { for entry in values { if entry == e { return true } } return false } /// Removes all values from this DataSet and recalculates min and max value. open override func clear() { values.removeAll(keepingCapacity: true) } // MARK: - Data functions and accessors // MARK: - NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! ChartDataSet copy.values = values copy._yMax = _yMax copy._yMin = _yMin return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/CombinedChartData.swift ================================================ // // CombinedChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class CombinedChartData: BarLineScatterCandleBubbleChartData { private var _lineData: LineChartData! private var _barData: BarChartData! private var _scatterData: ScatterChartData! private var _candleData: CandleChartData! private var _bubbleData: BubbleChartData! public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } @objc open var lineData: LineChartData! { get { return _lineData } set { _lineData = newValue notifyDataChanged() } } @objc open var barData: BarChartData! { get { return _barData } set { _barData = newValue notifyDataChanged() } } @objc open var scatterData: ScatterChartData! { get { return _scatterData } set { _scatterData = newValue notifyDataChanged() } } @objc open var candleData: CandleChartData! { get { return _candleData } set { _candleData = newValue notifyDataChanged() } } @objc open var bubbleData: BubbleChartData! { get { return _bubbleData } set { _bubbleData = newValue notifyDataChanged() } } open override func calcMinMax() { _dataSets.removeAll() _yMax = -Double.greatestFiniteMagnitude _yMin = Double.greatestFiniteMagnitude _xMax = -Double.greatestFiniteMagnitude _xMin = Double.greatestFiniteMagnitude _leftAxisMax = -Double.greatestFiniteMagnitude _leftAxisMin = Double.greatestFiniteMagnitude _rightAxisMax = -Double.greatestFiniteMagnitude _rightAxisMin = Double.greatestFiniteMagnitude let allData = self.allData for data in allData { data.calcMinMax() let sets = data.dataSets _dataSets.append(contentsOf: sets) if data.yMax > _yMax { _yMax = data.yMax } if data.yMin < _yMin { _yMin = data.yMin } if data.xMax > _xMax { _xMax = data.xMax } if data.xMin < _xMin { _xMin = data.xMin } for dataset in sets { if dataset.axisDependency == .left { if dataset.yMax > _leftAxisMax { _leftAxisMax = dataset.yMax } if dataset.yMin < _leftAxisMin { _leftAxisMin = dataset.yMin } } else { if dataset.yMax > _rightAxisMax { _rightAxisMax = dataset.yMax } if dataset.yMin < _rightAxisMin { _rightAxisMin = dataset.yMin } } } } } /// - returns: All data objects in row: line-bar-scatter-candle-bubble if not null. @objc open var allData: [ChartData] { var data = [ChartData]() if lineData !== nil { data.append(lineData) } if barData !== nil { data.append(barData) } if scatterData !== nil { data.append(scatterData) } if candleData !== nil { data.append(candleData) } if bubbleData !== nil { data.append(bubbleData) } return data } @objc open func dataByIndex(_ index: Int) -> ChartData { return allData[index] } open func dataIndex(_ data: ChartData) -> Int? { return allData.index(of: data) } open override func removeDataSet(_ dataSet: IChartDataSet!) -> Bool { let datas = allData var success = false for data in datas { success = data.removeDataSet(dataSet) if success { break } } return success } open override func removeDataSetByIndex(_ index: Int) -> Bool { print("removeDataSet(index) not supported for CombinedData", terminator: "\n") return false } open override func removeEntry(_ entry: ChartDataEntry, dataSetIndex: Int) -> Bool { print("removeEntry(entry, dataSetIndex) not supported for CombinedData", terminator: "\n") return false } open override func removeEntry(xValue: Double, dataSetIndex: Int) -> Bool { print("removeEntry(xValue, dataSetIndex) not supported for CombinedData", terminator: "\n") return false } open override func notifyDataChanged() { if _lineData !== nil { _lineData.notifyDataChanged() } if _barData !== nil { _barData.notifyDataChanged() } if _scatterData !== nil { _scatterData.notifyDataChanged() } if _candleData !== nil { _candleData.notifyDataChanged() } if _bubbleData !== nil { _bubbleData.notifyDataChanged() } super.notifyDataChanged() // recalculate everything } /// Get the Entry for a corresponding highlight object /// /// - parameter highlight: /// - returns: The entry that is highlighted open override func entryForHighlight(_ highlight: Highlight) -> ChartDataEntry? { if highlight.dataIndex >= allData.count { return nil } let data = dataByIndex(highlight.dataIndex) if highlight.dataSetIndex >= data.dataSetCount { return nil } // The value of the highlighted entry could be NaN - if we are not interested in highlighting a specific value. let entries = data.getDataSetByIndex(highlight.dataSetIndex).entriesForXValue(highlight.x) for e in entries { if e.y == highlight.y || highlight.y.isNaN { return e } } return nil } /// Get dataset for highlight /// /// - Parameter highlight: current highlight /// - Returns: dataset related to highlight @objc open func getDataSetByHighlight(_ highlight: Highlight) -> IChartDataSet! { if highlight.dataIndex >= allData.count { return nil } let data = dataByIndex(highlight.dataIndex) if highlight.dataSetIndex >= data.dataSetCount { return nil } return data.dataSets[highlight.dataSetIndex] } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/LineChartData.swift ================================================ // // LineChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation /// Data object that encapsulates all data associated with a LineChart. open class LineChartData: ChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/LineChartDataSet.swift ================================================ // // LineChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet { @objc(LineChartMode) public enum Mode: Int { case linear case stepped case cubicBezier case horizontalBezier } private func initialize() { // default color circleColors.append(NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)) } public required init() { super.init() initialize() } public override init(values: [ChartDataEntry]?, label: String?) { super.init(values: values, label: label) initialize() } // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// The drawing mode for this line dataset /// /// **default**: Linear open var mode: Mode = Mode.linear private var _cubicIntensity = CGFloat(0.2) /// Intensity for cubic lines (min = 0.05, max = 1) /// /// **default**: 0.2 open var cubicIntensity: CGFloat { get { return _cubicIntensity } set { _cubicIntensity = newValue if _cubicIntensity > 1.0 { _cubicIntensity = 1.0 } if _cubicIntensity < 0.05 { _cubicIntensity = 0.05 } } } /// The radius of the drawn circles. open var circleRadius = CGFloat(8.0) /// The hole radius of the drawn circles open var circleHoleRadius = CGFloat(4.0) open var circleColors = [NSUIColor]() /// - returns: The color at the given index of the DataSet's circle-color array. /// Performs a IndexOutOfBounds check by modulus. open func getCircleColor(atIndex index: Int) -> NSUIColor? { let size = circleColors.count let index = index % size if index >= size { return nil } return circleColors[index] } /// Sets the one and ONLY color that should be used for this DataSet. /// Internally, this recreates the colors array and adds the specified color. open func setCircleColor(_ color: NSUIColor) { circleColors.removeAll(keepingCapacity: false) circleColors.append(color) } open func setCircleColors(_ colors: NSUIColor...) { circleColors.removeAll(keepingCapacity: false) circleColors.append(contentsOf: colors) } /// Resets the circle-colors array and creates a new one open func resetCircleColors(_ index: Int) { circleColors.removeAll(keepingCapacity: false) } /// If true, drawing circles is enabled open var drawCirclesEnabled = true /// - returns: `true` if drawing circles for this DataSet is enabled, `false` ifnot open var isDrawCirclesEnabled: Bool { return drawCirclesEnabled } /// The color of the inner circle (the circle-hole). open var circleHoleColor: NSUIColor? = NSUIColor.white /// `true` if drawing circles for this DataSet is enabled, `false` ifnot open var drawCircleHoleEnabled = true /// - returns: `true` if drawing the circle-holes is enabled, `false` ifnot. open var isDrawCircleHoleEnabled: Bool { return drawCircleHoleEnabled } /// This is how much (in pixels) into the dash pattern are we starting from. open var lineDashPhase = CGFloat(0.0) /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] open var lineDashLengths: [CGFloat]? /// Line cap type, default is CGLineCap.Butt open var lineCapType = CGLineCap.butt /// formatter for customizing the position of the fill-line private var _fillFormatter: IFillFormatter = DefaultFillFormatter() /// Sets a custom IFillFormatter to the chart that handles the position of the filled-line for each DataSet. Set this to null to use the default logic. open var fillFormatter: IFillFormatter? { get { return _fillFormatter } set { _fillFormatter = newValue ?? DefaultFillFormatter() } } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! LineChartDataSet copy.circleColors = circleColors copy.circleRadius = circleRadius copy.cubicIntensity = cubicIntensity copy.lineDashPhase = lineDashPhase copy.lineDashLengths = lineDashLengths copy.lineCapType = lineCapType copy.drawCirclesEnabled = drawCirclesEnabled copy.drawCircleHoleEnabled = drawCircleHoleEnabled copy.mode = mode return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/LineRadarChartDataSet.swift ================================================ // // LineRadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class LineRadarChartDataSet: LineScatterCandleRadarChartDataSet, ILineRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// The color that is used for filling the line surface area. private var _fillColor = NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0) /// The color that is used for filling the line surface area. open var fillColor: NSUIColor { get { return _fillColor } set { _fillColor = newValue fill = nil } } /// The object that is used for filling the area below the line. /// **default**: nil open var fill: Fill? /// The alpha value that is used for filling the line surface, /// **default**: 0.33 open var fillAlpha = CGFloat(0.33) private var _lineWidth = CGFloat(1.0) /// line width of the chart (min = 0.0, max = 10) /// /// **default**: 1 open var lineWidth: CGFloat { get { return _lineWidth } set { if newValue < 0.0 { _lineWidth = 0.0 } else if newValue > 10.0 { _lineWidth = 10.0 } else { _lineWidth = newValue } } } /// Set to `true` if the DataSet should be drawn filled (surface), and not just as a line. /// Disabling this will give great performance boost. /// Please note that this method uses the path clipping for drawing the filled area (with images, gradients and layers). open var drawFilledEnabled = false /// - returns: `true` if filled drawing is enabled, `false` ifnot open var isDrawFilledEnabled: Bool { return drawFilledEnabled } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! LineRadarChartDataSet copy.fillColor = fillColor copy._lineWidth = _lineWidth copy.drawFilledEnabled = drawFilledEnabled return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/LineScatterCandleRadarChartDataSet.swift ================================================ // // LineScatterCandleRadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class LineScatterCandleRadarChartDataSet: BarLineScatterCandleBubbleChartDataSet, ILineScatterCandleRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn. open var drawHorizontalHighlightIndicatorEnabled = true /// Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn. open var drawVerticalHighlightIndicatorEnabled = true /// - returns: `true` if horizontal highlight indicator lines are enabled (drawn) open var isHorizontalHighlightIndicatorEnabled: Bool { return drawHorizontalHighlightIndicatorEnabled } /// - returns: `true` if vertical highlight indicator lines are enabled (drawn) open var isVerticalHighlightIndicatorEnabled: Bool { return drawVerticalHighlightIndicatorEnabled } /// Enables / disables both vertical and horizontal highlight-indicators. /// :param: enabled open func setDrawHighlightIndicators(_ enabled: Bool) { drawHorizontalHighlightIndicatorEnabled = enabled drawVerticalHighlightIndicatorEnabled = enabled } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! LineScatterCandleRadarChartDataSet copy.drawHorizontalHighlightIndicatorEnabled = drawHorizontalHighlightIndicatorEnabled copy.drawVerticalHighlightIndicatorEnabled = drawVerticalHighlightIndicatorEnabled return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/PieChartData.swift ================================================ // // PieData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation open class PieChartData: ChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } /// - returns: All DataSet objects this ChartData object holds. @objc open override var dataSets: [IChartDataSet] { get { assert(super.dataSets.count <= 1, "Found multiple data sets while pie chart only allows one") return super.dataSets } set { super.dataSets = newValue } } @objc var dataSet: IPieChartDataSet? { get { return dataSets.count > 0 ? dataSets[0] as? IPieChartDataSet : nil } set { if let newValue = newValue { dataSets = [newValue] } else { dataSets = [] } } } open override func getDataSetByIndex(_ index: Int) -> IChartDataSet? { if index != 0 { return nil } return super.getDataSetByIndex(index) } open override func getDataSetByLabel(_ label: String, ignorecase: Bool) -> IChartDataSet? { if dataSets.count == 0 || dataSets[0].label == nil { return nil } if ignorecase { if let label = dataSets[0].label, label.caseInsensitiveCompare(label) == .orderedSame { return dataSets[0] } } else { if label == dataSets[0].label { return dataSets[0] } } return nil } open override func entryForHighlight(_ highlight: Highlight) -> ChartDataEntry? { return dataSet?.entryForIndex(Int(highlight.x)) } open override func addDataSet(_ d: IChartDataSet!) { super.addDataSet(d) } /// Removes the DataSet at the given index in the DataSet array from the data object. /// Also recalculates all minimum and maximum values. /// /// - returns: `true` if a DataSet was removed, `false` ifno DataSet could be removed. open override func removeDataSetByIndex(_ index: Int) -> Bool { if index >= _dataSets.count || index < 0 { return false } return false } /// - returns: The total y-value sum across all DataSet objects the this object represents. @objc open var yValueSum: Double { guard let dataSet = dataSet else { return 0.0 } var yValueSum: Double = 0.0 for i in 0.. AnyObject { let copy = super.copyWithZone(zone) as! PieChartDataEntry copy.label = label return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/PieChartDataSet.swift ================================================ // // PieChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class PieChartDataSet: ChartDataSet, IPieChartDataSet { @objc(PieChartValuePosition) public enum ValuePosition: Int { case insideSlice case outsideSlice } private func initialize() { self.valueTextColor = NSUIColor.white self.valueFont = NSUIFont.systemFont(ofSize: 13.0) } public required init() { super.init() initialize() } public override init(values: [ChartDataEntry]?, label: String?) { super.init(values: values, label: label) initialize() } internal override func calcMinMax(entry e: ChartDataEntry) { calcMinMaxY(entry: e) } // MARK: - Styling functions and accessors private var _sliceSpace = CGFloat(0.0) /// the space in pixels between the pie-slices /// **default**: 0 /// **maximum**: 20 open var sliceSpace: CGFloat { get { return _sliceSpace } set { var space = newValue if space > 20.0 { space = 20.0 } if space < 0.0 { space = 0.0 } _sliceSpace = space } } /// When enabled, slice spacing will be 0.0 when the smallest value is going to be smaller than the slice spacing itself. open var automaticallyDisableSliceSpacing: Bool = false /// indicates the selection distance of a pie slice open var selectionShift = CGFloat(18.0) open var xValuePosition: ValuePosition = .insideSlice open var yValuePosition: ValuePosition = .insideSlice /// When valuePosition is OutsideSlice, indicates line color open var valueLineColor: NSUIColor? = NSUIColor.black /// When valuePosition is OutsideSlice, indicates line width open var valueLineWidth: CGFloat = 1.0 /// When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size open var valueLinePart1OffsetPercentage: CGFloat = 0.75 /// When valuePosition is OutsideSlice, indicates length of first half of the line open var valueLinePart1Length: CGFloat = 0.3 /// When valuePosition is OutsideSlice, indicates length of second half of the line open var valueLinePart2Length: CGFloat = 0.4 /// When valuePosition is OutsideSlice, this allows variable line length open var valueLineVariableLength: Bool = true /// the font for the slice-text labels open var entryLabelFont: NSUIFont? = nil /// the color for the slice-text labels open var entryLabelColor: NSUIColor? = nil /// the color for the highlighted sector open var highlightColor: NSUIColor? = nil // MARK: - NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! PieChartDataSet copy._sliceSpace = _sliceSpace copy.selectionShift = selectionShift copy.highlightColor = highlightColor return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartData.swift ================================================ // // RadarChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class RadarChartData: ChartData { @objc open var highlightColor = NSUIColor(red: 255.0/255.0, green: 187.0/255.0, blue: 115.0/255.0, alpha: 1.0) @objc open var highlightLineWidth = CGFloat(1.0) @objc open var highlightLineDashPhase = CGFloat(0.0) @objc open var highlightLineDashLengths: [CGFloat]? /// Sets labels that should be drawn around the RadarChart at the end of each web line. @objc open var labels = [String]() /// Sets the labels that should be drawn around the RadarChart at the end of each web line. open func setLabels(_ labels: String...) { self.labels = labels } public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } open override func entryForHighlight(_ highlight: Highlight) -> ChartDataEntry? { return getDataSetByIndex(highlight.dataSetIndex)?.entryForIndex(Int(highlight.x)) } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartDataEntry.swift ================================================ // // RadarChartDataEntry.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class RadarChartDataEntry: ChartDataEntry { public required init() { super.init() } /// - parameter value: The value on the y-axis. /// - parameter data: Spot for additional data this Entry represents. @objc public init(value: Double, data: AnyObject?) { super.init(x: 0.0, y: value, data: data) } /// - parameter value: The value on the y-axis. @objc public convenience init(value: Double) { self.init(value: value, data: nil) } // MARK: Data property accessors @objc open var value: Double { get { return y } set { y = value } } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! RadarChartDataEntry return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/RadarChartDataSet.swift ================================================ // // RadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class RadarChartDataSet: LineRadarChartDataSet, IRadarChartDataSet { private func initialize() { self.valueFont = NSUIFont.systemFont(ofSize: 13.0) } public required init() { super.init() initialize() } public required override init(values: [ChartDataEntry]?, label: String?) { super.init(values: values, label: label) initialize() } // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// flag indicating whether highlight circle should be drawn or not /// **default**: false open var drawHighlightCircleEnabled: Bool = false /// - returns: `true` if highlight circle should be drawn, `false` ifnot open var isDrawHighlightCircleEnabled: Bool { return drawHighlightCircleEnabled } open var highlightCircleFillColor: NSUIColor? = NSUIColor.white /// The stroke color for highlight circle. /// If `nil`, the color of the dataset is taken. open var highlightCircleStrokeColor: NSUIColor? open var highlightCircleStrokeAlpha: CGFloat = 0.3 open var highlightCircleInnerRadius: CGFloat = 3.0 open var highlightCircleOuterRadius: CGFloat = 4.0 open var highlightCircleStrokeWidth: CGFloat = 2.0 } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ScatterChartData.swift ================================================ // // ScatterChartData.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ScatterChartData: BarLineScatterCandleBubbleChartData { public override init() { super.init() } public override init(dataSets: [IChartDataSet]?) { super.init(dataSets: dataSets) } /// - returns: The maximum shape-size across all DataSets. @objc open func getGreatestShapeSize() -> CGFloat { var max = CGFloat(0.0) for set in _dataSets { let scatterDataSet = set as? IScatterChartDataSet if scatterDataSet == nil { print("ScatterChartData: Found a DataSet which is not a ScatterChartDataSet", terminator: "\n") } else if let size = scatterDataSet?.scatterShapeSize, size > max { max = size } } return max } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Implementations/Standard/ScatterChartDataSet.swift ================================================ // // ScatterChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ScatterChartDataSet: LineScatterCandleRadarChartDataSet, IScatterChartDataSet { @objc(ScatterShape) public enum Shape: Int { case square case circle case triangle case cross case x case chevronUp case chevronDown } /// The size the scatter shape will have open var scatterShapeSize = CGFloat(10.0) /// The radius of the hole in the shape (applies to Square, Circle and Triangle) /// **default**: 0.0 open var scatterShapeHoleRadius: CGFloat = 0.0 /// Color for the hole in the shape. Setting to `nil` will behave as transparent. /// **default**: nil open var scatterShapeHoleColor: NSUIColor? = nil /// Sets the ScatterShape this DataSet should be drawn with. /// This will search for an available IShapeRenderer and set this renderer for the DataSet @objc open func setScatterShape(_ shape: Shape) { self.shapeRenderer = ScatterChartDataSet.renderer(forShape: shape) } /// The IShapeRenderer responsible for rendering this DataSet. /// This can also be used to set a custom IShapeRenderer aside from the default ones. /// **default**: `SquareShapeRenderer` open var shapeRenderer: IShapeRenderer? = SquareShapeRenderer() @objc open class func renderer(forShape shape: Shape) -> IShapeRenderer { switch shape { case .square: return SquareShapeRenderer() case .circle: return CircleShapeRenderer() case .triangle: return TriangleShapeRenderer() case .cross: return CrossShapeRenderer() case .x: return XShapeRenderer() case .chevronUp: return ChevronUpShapeRenderer() case .chevronDown: return ChevronDownShapeRenderer() } } // MARK: NSCopying open override func copyWithZone(_ zone: NSZone?) -> AnyObject { let copy = super.copyWithZone(zone) as! ScatterChartDataSet copy.scatterShapeSize = scatterShapeSize copy.scatterShapeHoleRadius = scatterShapeHoleRadius copy.scatterShapeHoleColor = scatterShapeHoleColor copy.shapeRenderer = shapeRenderer return copy } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IBarChartDataSet.swift ================================================ // // IBarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IBarChartDataSet: IBarLineScatterCandleBubbleChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// - returns: `true` if this DataSet is stacked (stacksize > 1) or not. var isStacked: Bool { get } /// - returns: The maximum number of bars that can be stacked upon another in this DataSet. var stackSize: Int { get } /// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value var barShadowColor: NSUIColor { get set } /// the width used for drawing borders around the bars. If borderWidth == 0, no border will be drawn. var barBorderWidth : CGFloat { get set } /// the color drawing borders around the bars. var barBorderColor: NSUIColor { get set } /// the alpha value (transparency) that is used for drawing the highlight indicator bar. min = 0.0 (fully transparent), max = 1.0 (fully opaque) var highlightAlpha: CGFloat { get set } /// array of labels used to describe the different values of the stacked bars var stackLabels: [String] { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IBarLineScatterCandleBubbleChartDataSet.swift ================================================ // // IBarLineScatterCandleBubbleChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IBarLineScatterCandleBubbleChartDataSet: IChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors var highlightColor: NSUIColor { get set } var highlightLineWidth: CGFloat { get set } var highlightLineDashPhase: CGFloat { get set } var highlightLineDashLengths: [CGFloat]? { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IBubbleChartDataSet.swift ================================================ // // IBubbleChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IBubbleChartDataSet: IBarLineScatterCandleBubbleChartDataSet { // MARK: - Data functions and accessors var maxSize: CGFloat { get } var isNormalizeSizeEnabled: Bool { get } // MARK: - Styling functions and accessors /// Sets/gets the width of the circle that surrounds the bubble when highlighted var highlightCircleWidth: CGFloat { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/ICandleChartDataSet.swift ================================================ // // ICandleChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol ICandleChartDataSet: ILineScatterCandleRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// the space that is left out on the left and right side of each candle, /// **default**: 0.1 (10%), max 0.45, min 0.0 var barSpace: CGFloat { get set } /// should the candle bars show? /// when false, only "ticks" will show /// /// **default**: true var showCandleBar: Bool { get set } /// the width of the candle-shadow-line in pixels. /// /// **default**: 3.0 var shadowWidth: CGFloat { get set } /// the color of the shadow line var shadowColor: NSUIColor? { get set } /// use candle color for the shadow var shadowColorSameAsCandle: Bool { get set } /// Is the shadow color same as the candle color? var isShadowColorSameAsCandle: Bool { get } /// color for open == close var neutralColor: NSUIColor? { get set } /// color for open > close var increasingColor: NSUIColor? { get set } /// color for open < close var decreasingColor: NSUIColor? { get set } /// Are increasing values drawn as filled? var increasingFilled: Bool { get set } /// Are increasing values drawn as filled? var isIncreasingFilled: Bool { get } /// Are decreasing values drawn as filled? var decreasingFilled: Bool { get set } /// Are decreasing values drawn as filled? var isDecreasingFilled: Bool { get } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IChartDataSet.swift ================================================ // // IChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IChartDataSet { // MARK: - Data functions and accessors /// Use this method to tell the data set that the underlying data has changed func notifyDataSetChanged() /// Calculates the minimum and maximum x and y values (_xMin, _xMax, _yMin, _yMax). func calcMinMax() /// Calculates the min and max y-values from the Entry closest to the given fromX to the Entry closest to the given toX value. /// This is only needed for the autoScaleMinMax feature. func calcMinMaxY(fromX: Double, toX: Double) /// - returns: The minimum y-value this DataSet holds var yMin: Double { get } /// - returns: The maximum y-value this DataSet holds var yMax: Double { get } /// - returns: The minimum x-value this DataSet holds var xMin: Double { get } /// - returns: The maximum x-value this DataSet holds var xMax: Double { get } /// - returns: The number of y-values this DataSet represents var entryCount: Int { get } /// - returns: The entry object found at the given index (not x-value!) /// - throws: out of bounds /// if `i` is out of bounds, it may throw an out-of-bounds exception func entryForIndex(_ i: Int) -> ChartDataEntry? /// - returns: The first Entry object found at the given x-value with binary search. /// If the no Entry at the specified x-value is found, this method returns the Entry at the closest x-value according to the rounding. /// nil if no Entry object at that x-value. /// - parameter xValue: the x-value /// - parameter closestToY: If there are multiple y-values for the specified x-value, /// - parameter rounding: determine whether to round up/down/closest if there is no Entry matching the provided x-value func entryForXValue( _ xValue: Double, closestToY yValue: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? /// - returns: The first Entry object found at the given x-value with binary search. /// If the no Entry at the specified x-value is found, this method returns the Entry at the closest x-value. /// nil if no Entry object at that x-value. /// - parameter xValue: the x-value /// - parameter closestToY: If there are multiple y-values for the specified x-value, func entryForXValue( _ xValue: Double, closestToY yValue: Double) -> ChartDataEntry? /// - returns: All Entry objects found at the given x-value with binary search. /// An empty array if no Entry object at that x-value. func entriesForXValue(_ xValue: Double) -> [ChartDataEntry] /// - returns: The array-index of the specified entry. /// If the no Entry at the specified x-value is found, this method returns the index of the Entry at the closest x-value according to the rounding. /// /// - parameter xValue: x-value of the entry to search for /// - parameter closestToY: If there are multiple y-values for the specified x-value, /// - parameter rounding: Rounding method if exact value was not found func entryIndex( x xValue: Double, closestToY yValue: Double, rounding: ChartDataSetRounding) -> Int /// - returns: The array-index of the specified entry /// /// - parameter e: the entry to search for func entryIndex(entry e: ChartDataEntry) -> Int /// Adds an Entry to the DataSet dynamically. /// /// *optional feature, can return `false` ifnot implemented* /// /// Entries are added to the end of the list. /// - parameter e: the entry to add /// - returns: `true` if the entry was added successfully, `false` ifthis feature is not supported func addEntry(_ e: ChartDataEntry) -> Bool /// Adds an Entry to the DataSet dynamically. /// Entries are added to their appropriate index in the values array respective to their x-position. /// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum. /// /// *optional feature, can return `false` ifnot implemented* /// /// Entries are added to the end of the list. /// - parameter e: the entry to add /// - returns: `true` if the entry was added successfully, `false` ifthis feature is not supported func addEntryOrdered(_ e: ChartDataEntry) -> Bool /// Removes an Entry from the DataSet dynamically. /// /// *optional feature, can return `false` ifnot implemented* /// /// - parameter entry: the entry to remove /// - returns: `true` if the entry was removed successfully, `false` ifthe entry does not exist or if this feature is not supported func removeEntry(_ entry: ChartDataEntry) -> Bool /// Removes the Entry object at the given index in the values array from the DataSet. /// /// *optional feature, can return `false` ifnot implemented* /// /// - parameter index: the index of the entry to remove /// - returns: `true` if the entry was removed successfully, `false` ifthe entry does not exist or if this feature is not supported func removeEntry(index: Int) -> Bool /// Removes the Entry object closest to the given x-value from the DataSet. /// /// *optional feature, can return `false` ifnot implemented* /// /// - parameter x: the x-value to remove /// - returns: `true` if the entry was removed successfully, `false` ifthe entry does not exist or if this feature is not supported func removeEntry(x: Double) -> Bool /// Removes the first Entry (at index 0) of this DataSet from the entries array. /// /// *optional feature, can return `false` ifnot implemented* /// /// - returns: `true` if the entry was removed successfully, `false` ifthe entry does not exist or if this feature is not supported func removeFirst() -> Bool /// Removes the last Entry (at index 0) of this DataSet from the entries array. /// /// *optional feature, can return `false` ifnot implemented* /// /// - returns: `true` if the entry was removed successfully, `false` ifthe entry does not exist or if this feature is not supported func removeLast() -> Bool /// Checks if this DataSet contains the specified Entry. /// /// - returns: `true` if contains the entry, `false` ifnot. func contains(_ e: ChartDataEntry) -> Bool /// Removes all values from this DataSet and does all necessary recalculations. /// /// *optional feature, could throw if not implemented* func clear() // MARK: - Styling functions and accessors /// The label string that describes the DataSet. var label: String? { get } /// The axis this DataSet should be plotted against. var axisDependency: YAxis.AxisDependency { get } /// List representing all colors that are used for drawing the actual values for this DataSet var valueColors: [NSUIColor] { get } /// All the colors that are used for this DataSet. /// Colors are reused as soon as the number of Entries the DataSet represents is higher than the size of the colors array. var colors: [NSUIColor] { get } /// - returns: The color at the given index of the DataSet's color array. /// This prevents out-of-bounds by performing a modulus on the color index, so colours will repeat themselves. func color(atIndex: Int) -> NSUIColor func resetColors() func addColor(_ color: NSUIColor) func setColor(_ color: NSUIColor) /// if true, value highlighting is enabled var highlightEnabled: Bool { get set } /// - returns: `true` if value highlighting is enabled for this dataset var isHighlightEnabled: Bool { get } /// Custom formatter that is used instead of the auto-formatter if set var valueFormatter: IValueFormatter? { get set } /// - returns: `true` if the valueFormatter object of this DataSet is null. var needsFormatter: Bool { get } /// Sets/get a single color for value text. /// Setting the color clears the colors array and adds a single color. /// Getting will return the first color in the array. var valueTextColor: NSUIColor { get set } /// - returns: The color at the specified index that is used for drawing the values inside the chart. Uses modulus internally. func valueTextColorAt(_ index: Int) -> NSUIColor /// the font for the value-text labels var valueFont: NSUIFont { get set } /// The form to draw for this dataset in the legend. /// /// Return `.Default` to use the default legend form. var form: Legend.Form { get } /// The form size to draw for this dataset in the legend. /// /// Return `NaN` to use the default legend form size. var formSize: CGFloat { get } /// The line width for drawing the form of this dataset in the legend /// /// Return `NaN` to use the default legend form line width. var formLineWidth: CGFloat { get } /// Line dash configuration for legend shapes that consist of lines. /// /// This is how much (in pixels) into the dash pattern are we starting from. var formLineDashPhase: CGFloat { get } /// Line dash configuration for legend shapes that consist of lines. /// /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] var formLineDashLengths: [CGFloat]? { get } /// Set this to true to draw y-values on the chart. /// /// - note: For bar and line charts: if `maxVisibleCount` is reached, no values will be drawn even if this is enabled. var drawValuesEnabled: Bool { get set } /// - returns: `true` if y-value drawing is enabled, `false` ifnot var isDrawValuesEnabled: Bool { get } /// Set this to true to draw y-icons on the chart /// /// - note: For bar and line charts: if `maxVisibleCount` is reached, no icons will be drawn even if this is enabled. var drawIconsEnabled: Bool { get set } /// Returns true if y-icon drawing is enabled, false if not var isDrawIconsEnabled: Bool { get } /// Offset of icons drawn on the chart. /// /// For all charts except Pie and Radar it will be ordinary (x offset, y offset). /// /// For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint. var iconsOffset: CGPoint { get set } /// Set the visibility of this DataSet. If not visible, the DataSet will not be drawn to the chart upon refreshing it. var visible: Bool { get set } /// - returns: `true` if this DataSet is visible inside the chart, or `false` ifit is currently hidden. var isVisible: Bool { get } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/ILineChartDataSet.swift ================================================ // // ILineChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol ILineChartDataSet: ILineRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// The drawing mode for this line dataset /// /// **default**: Linear var mode: LineChartDataSet.Mode { get set } /// Intensity for cubic lines (min = 0.05, max = 1) /// /// **default**: 0.2 var cubicIntensity: CGFloat { get set } /// The radius of the drawn circles. var circleRadius: CGFloat { get set } /// The hole radius of the drawn circles. var circleHoleRadius: CGFloat { get set } var circleColors: [NSUIColor] { get set } /// - returns: The color at the given index of the DataSet's circle-color array. /// Performs a IndexOutOfBounds check by modulus. func getCircleColor(atIndex: Int) -> NSUIColor? /// Sets the one and ONLY color that should be used for this DataSet. /// Internally, this recreates the colors array and adds the specified color. func setCircleColor(_ color: NSUIColor) /// Resets the circle-colors array and creates a new one func resetCircleColors(_ index: Int) /// If true, drawing circles is enabled var drawCirclesEnabled: Bool { get set } /// - returns: `true` if drawing circles for this DataSet is enabled, `false` ifnot var isDrawCirclesEnabled: Bool { get } /// The color of the inner circle (the circle-hole). var circleHoleColor: NSUIColor? { get set } /// `true` if drawing circles for this DataSet is enabled, `false` ifnot var drawCircleHoleEnabled: Bool { get set } /// - returns: `true` if drawing the circle-holes is enabled, `false` ifnot. var isDrawCircleHoleEnabled: Bool { get } /// This is how much (in pixels) into the dash pattern are we starting from. var lineDashPhase: CGFloat { get } /// This is the actual dash pattern. /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] var lineDashLengths: [CGFloat]? { get set } /// Line cap type, default is CGLineCap.Butt var lineCapType: CGLineCap { get set } /// Sets a custom IFillFormatter to the chart that handles the position of the filled-line for each DataSet. Set this to null to use the default logic. var fillFormatter: IFillFormatter? { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/ILineRadarChartDataSet.swift ================================================ // // ILineRadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol ILineRadarChartDataSet: ILineScatterCandleRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// The color that is used for filling the line surface area. var fillColor: NSUIColor { get set } /// - returns: The object that is used for filling the area below the line. /// **default**: nil var fill: Fill? { get set } /// The alpha value that is used for filling the line surface. /// **default**: 0.33 var fillAlpha: CGFloat { get set } /// line width of the chart (min = 0.0, max = 10) /// /// **default**: 1 var lineWidth: CGFloat { get set } /// Set to `true` if the DataSet should be drawn filled (surface), and not just as a line. /// Disabling this will give great performance boost. /// Please note that this method uses the path clipping for drawing the filled area (with images, gradients and layers). var drawFilledEnabled: Bool { get set } /// - returns: `true` if filled drawing is enabled, `false` if not var isDrawFilledEnabled: Bool { get } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/ILineScatterCandleRadarChartDataSet.swift ================================================ // // ILineScatterCandleRadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation @objc public protocol ILineScatterCandleRadarChartDataSet: IBarLineScatterCandleBubbleChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn. var drawHorizontalHighlightIndicatorEnabled: Bool { get set } /// Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn. var drawVerticalHighlightIndicatorEnabled: Bool { get set } /// - returns: `true` if horizontal highlight indicator lines are enabled (drawn) var isHorizontalHighlightIndicatorEnabled: Bool { get } /// - returns: `true` if vertical highlight indicator lines are enabled (drawn) var isVerticalHighlightIndicatorEnabled: Bool { get } /// Enables / disables both vertical and horizontal highlight-indicators. /// :param: enabled func setDrawHighlightIndicators(_ enabled: Bool) } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IPieChartDataSet.swift ================================================ // // IPieChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc public protocol IPieChartDataSet: IChartDataSet { // MARK: - Styling functions and accessors /// the space in pixels between the pie-slices /// **default**: 0 /// **maximum**: 20 var sliceSpace: CGFloat { get set } /// When enabled, slice spacing will be 0.0 when the smallest value is going to be smaller than the slice spacing itself. var automaticallyDisableSliceSpacing: Bool { get set } /// indicates the selection distance of a pie slice var selectionShift: CGFloat { get set } var xValuePosition: PieChartDataSet.ValuePosition { get set } var yValuePosition: PieChartDataSet.ValuePosition { get set } /// When valuePosition is OutsideSlice, indicates line color var valueLineColor: NSUIColor? { get set } /// When valuePosition is OutsideSlice, indicates line width var valueLineWidth: CGFloat { get set } /// When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size var valueLinePart1OffsetPercentage: CGFloat { get set } /// When valuePosition is OutsideSlice, indicates length of first half of the line var valueLinePart1Length: CGFloat { get set } /// When valuePosition is OutsideSlice, indicates length of second half of the line var valueLinePart2Length: CGFloat { get set } /// When valuePosition is OutsideSlice, this allows variable line length var valueLineVariableLength: Bool { get set } /// the font for the slice-text labels var entryLabelFont: NSUIFont? { get set } /// the color for the slice-text labels var entryLabelColor: NSUIColor? { get set } /// get/sets the color for the highlighted sector var highlightColor: NSUIColor? { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IRadarChartDataSet.swift ================================================ // // IRadarChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IRadarChartDataSet: ILineRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// flag indicating whether highlight circle should be drawn or not var drawHighlightCircleEnabled: Bool { get set } var isDrawHighlightCircleEnabled: Bool { get } var highlightCircleFillColor: NSUIColor? { get set } /// The stroke color for highlight circle. /// If `nil`, the color of the dataset is taken. var highlightCircleStrokeColor: NSUIColor? { get set } var highlightCircleStrokeAlpha: CGFloat { get set } var highlightCircleInnerRadius: CGFloat { get set } var highlightCircleOuterRadius: CGFloat { get set } var highlightCircleStrokeWidth: CGFloat { get set } } ================================================ FILE: Pods/Charts/Source/Charts/Data/Interfaces/IScatterChartDataSet.swift ================================================ // // IScatterChartDataSet.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IScatterChartDataSet: ILineScatterCandleRadarChartDataSet { // MARK: - Data functions and accessors // MARK: - Styling functions and accessors /// - returns: The size the scatter shape will have var scatterShapeSize: CGFloat { get } /// - returns: The radius of the hole in the shape (applies to Square, Circle and Triangle) /// Set this to <= 0 to remove holes. /// **default**: 0.0 var scatterShapeHoleRadius: CGFloat { get } /// - returns: Color for the hole in the shape. Setting to `nil` will behave as transparent. /// **default**: nil var scatterShapeHoleColor: NSUIColor? { get } /// - returns: The IShapeRenderer responsible for rendering this DataSet. var shapeRenderer: IShapeRenderer? { get } } ================================================ FILE: Pods/Charts/Source/Charts/Filters/DataApproximator+N.swift ================================================ // // DataApproximator+N.swift // Charts // // Created by M Ivaniushchenko on 9/6/17. // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation extension CGPoint { fileprivate func distanceToLine(from linePoint1: CGPoint, to linePoint2: CGPoint) -> CGFloat { let dx = linePoint2.x - linePoint1.x let dy = linePoint2.y - linePoint1.y let dividend = fabs(dy * self.x - dx * self.y - linePoint1.x * linePoint2.y + linePoint2.x * linePoint1.y) let divisor = sqrt(dx * dx + dy * dy) return dividend / divisor } } private struct LineAlt { let start: Int let end: Int var distance: CGFloat = 0 var index: Int = 0 init(start: Int, end: Int, points: [CGPoint]) { self.start = start self.end = end let startPoint = points[start] let endPoint = points[end] guard (end > start + 1) else { return } for i in start + 1 ..< end { let currentPoint = points[i] let distance = currentPoint.distanceToLine(from: startPoint, to: endPoint) if distance > self.distance { self.index = i self.distance = distance } } } } extension LineAlt: Comparable { static func ==(lhs: LineAlt, rhs: LineAlt) -> Bool { return (lhs.start == rhs.start) && (lhs.end == rhs.end) && (lhs.index == rhs.index) } static func <(lhs: LineAlt, rhs: LineAlt) -> Bool { return lhs.distance < rhs.distance } } extension DataApproximator { /// uses the douglas peuker algorithm to reduce the given arraylist of entries to given number of points /// More algorithm details here - http://psimpl.sourceforge.net/douglas-peucker.html @objc open class func reduceWithDouglasPeukerN(_ points: [CGPoint], resultCount: Int) -> [CGPoint] { // if a shape has 2 or less points it cannot be reduced if resultCount <= 2 || resultCount >= points.count { return points } var keep = [Bool](repeating: false, count: points.count) // first and last always stay keep[0] = true keep[points.count - 1] = true var currentStoredPoints = 2 var queue = [LineAlt]() let line = LineAlt(start: 0, end: points.count - 1, points: points) queue.append(line) repeat { let line = queue.popLast()! // store the key keep[line.index] = true // check point count tolerance currentStoredPoints += 1 if (currentStoredPoints == resultCount) { break; } // split the polyline at the key and recurse let left = LineAlt(start: line.start, end: line.index, points: points) if (left.index > 0) { self.insertLine(left, into: &queue) } let right = LineAlt(start: line.index, end: line.end, points: points) if (right.index > 0) { self.insertLine(right, into: &queue) } } while !queue.isEmpty // create a new array with series, only take the kept ones let reducedEntries = points.enumerated().compactMap { (index: Int, point: CGPoint) -> CGPoint? in return keep[index] ? point : nil } return reducedEntries } // Keeps array sorted private static func insertLine(_ line: LineAlt, into array: inout [LineAlt]) { let insertionIndex = self.insertionIndex(for: line, into: &array) array.insert(line, at: insertionIndex) } private static func insertionIndex(for line: LineAlt, into array: inout [LineAlt]) -> Int { var indices = array.indices while !indices.isEmpty { let midIndex = indices.lowerBound.advanced(by: indices.count / 2) let midLine = array[midIndex] if midLine == line { return midIndex } else if (line < midLine) { // perform search in left half indices = indices.lowerBound.. [CGPoint] { // if a shape has 2 or less points it cannot be reduced if tolerance <= 0 || points.count < 3 { return points } var keep = [Bool](repeating: false, count: points.count) // first and last always stay keep[0] = true keep[points.count - 1] = true // first and last entry are entry point to recursion reduceWithDouglasPeuker(points: points, tolerance: tolerance, start: 0, end: points.count - 1, keep: &keep) // create a new array with series, only take the kept ones var reducedEntries = [CGPoint]() for i in 0 ..< points.count { if keep[i] { reducedEntries.append(points[i]) } } return reducedEntries } /// apply the Douglas-Peucker-Reduction to an array of `CGPoint`s with a given tolerance /// /// - parameter points: /// - parameter tolerance: /// - parameter start: /// - parameter end: open class func reduceWithDouglasPeuker( points: [CGPoint], tolerance: CGFloat, start: Int, end: Int, keep: inout [Bool]) { if end <= start + 1 { // recursion finished return } var greatestIndex = Int(0) var greatestDistance = CGFloat(0.0) let line = Line(pt1: points[start], pt2: points[end]) for i in start + 1 ..< end { let distance = line.distance(toPoint: points[i]) if distance > greatestDistance { greatestDistance = distance greatestIndex = i } } if greatestDistance > tolerance { // keep max dist point keep[greatestIndex] = true // recursive call reduceWithDouglasPeuker(points: points, tolerance: tolerance, start: start, end: greatestIndex, keep: &keep) reduceWithDouglasPeuker(points: points, tolerance: tolerance, start: greatestIndex, end: end, keep: &keep) } // else don't keep the point... } private class Line { var sxey: CGFloat var exsy: CGFloat var dx: CGFloat var dy: CGFloat var length: CGFloat init(pt1: CGPoint, pt2: CGPoint) { dx = pt1.x - pt2.x dy = pt1.y - pt2.y sxey = pt1.x * pt2.y exsy = pt2.x * pt1.y length = sqrt(dx * dx + dy * dy) } func distance(toPoint pt: CGPoint) -> CGFloat { return abs(dy * pt.x - dx * pt.y + sxey - exsy) / length } } } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/DefaultAxisValueFormatter.swift ================================================ // // DefaultAxisValueFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation @objc(ChartDefaultAxisValueFormatter) open class DefaultAxisValueFormatter: NSObject, IAxisValueFormatter { public typealias Block = ( _ value: Double, _ axis: AxisBase?) -> String @objc open var block: Block? @objc open var hasAutoDecimals: Bool = false private var _formatter: NumberFormatter? @objc open var formatter: NumberFormatter? { get { return _formatter } set { hasAutoDecimals = false _formatter = newValue } } // TODO: Documentation. Especially the nil case private var _decimals: Int? open var decimals: Int? { get { return _decimals } set { _decimals = newValue if let digits = newValue { self.formatter?.minimumFractionDigits = digits self.formatter?.maximumFractionDigits = digits self.formatter?.usesGroupingSeparator = true } } } public override init() { super.init() self.formatter = NumberFormatter() hasAutoDecimals = true } @objc public init(formatter: NumberFormatter) { super.init() self.formatter = formatter } @objc public init(decimals: Int) { super.init() self.formatter = NumberFormatter() self.formatter?.usesGroupingSeparator = true self.decimals = decimals hasAutoDecimals = true } @objc public init(block: @escaping Block) { super.init() self.block = block } @objc public static func with(block: @escaping Block) -> DefaultAxisValueFormatter? { return DefaultAxisValueFormatter(block: block) } open func stringForValue(_ value: Double, axis: AxisBase?) -> String { if let block = block { return block(value, axis) } else { return formatter?.string(from: NSNumber(floatLiteral: value)) ?? "" } } } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/DefaultFillFormatter.swift ================================================ // // DefaultFillFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif /// Default formatter that calculates the position of the filled line. @objc(ChartDefaultFillFormatter) open class DefaultFillFormatter: NSObject, IFillFormatter { public typealias Block = ( _ dataSet: ILineChartDataSet, _ dataProvider: LineChartDataProvider) -> CGFloat @objc open var block: Block? public override init() { } @objc public init(block: @escaping Block) { self.block = block } @objc public static func with(block: @escaping Block) -> DefaultFillFormatter? { return DefaultFillFormatter(block: block) } open func getFillLinePosition( dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat { guard block == nil else { return block!(dataSet, dataProvider) } var fillMin: CGFloat = 0.0 if dataSet.yMax > 0.0 && dataSet.yMin < 0.0 { fillMin = 0.0 } else if let data = dataProvider.data { let max = data.yMax > 0.0 ? 0.0 : dataProvider.chartYMax let min = data.yMin < 0.0 ? 0.0 : dataProvider.chartYMin fillMin = CGFloat(dataSet.yMin >= 0.0 ? min : max) } return fillMin } } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/DefaultValueFormatter.swift ================================================ // // DefaultValueFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation @objc(ChartDefaultValueFormatter) open class DefaultValueFormatter: NSObject, IValueFormatter { public typealias Block = ( _ value: Double, _ entry: ChartDataEntry, _ dataSetIndex: Int, _ viewPortHandler: ViewPortHandler?) -> String @objc open var block: Block? @objc open var hasAutoDecimals: Bool = false private var _formatter: NumberFormatter? @objc open var formatter: NumberFormatter? { get { return _formatter } set { hasAutoDecimals = false _formatter = newValue } } private var _decimals: Int? open var decimals: Int? { get { return _decimals } set { _decimals = newValue if let digits = newValue { self.formatter?.minimumFractionDigits = digits self.formatter?.maximumFractionDigits = digits self.formatter?.usesGroupingSeparator = true } } } public override init() { super.init() self.formatter = NumberFormatter() hasAutoDecimals = true } @objc public init(formatter: NumberFormatter) { super.init() self.formatter = formatter } @objc public init(decimals: Int) { super.init() self.formatter = NumberFormatter() self.formatter?.usesGroupingSeparator = true self.decimals = decimals hasAutoDecimals = true } @objc public init(block: @escaping Block) { super.init() self.block = block } @objc public static func with(block: @escaping Block) -> DefaultValueFormatter? { return DefaultValueFormatter(block: block) } open func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String { if let block = block { return block(value, entry, dataSetIndex, viewPortHandler) } else { return formatter?.string(from: NSNumber(floatLiteral: value)) ?? "" } } } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/IAxisValueFormatter.swift ================================================ // // IAxisValueFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation /// An interface for providing custom axis Strings. @objc(IChartAxisValueFormatter) public protocol IAxisValueFormatter: class { /// Called when a value from an axis is formatted before being drawn. /// /// For performance reasons, avoid excessive calculations and memory allocations inside this method. /// /// - returns: The customized label that is drawn on the x-axis. /// - parameter value: the value that is currently being drawn /// - parameter axis: the axis that the value belongs to /// func stringForValue(_ value: Double, axis: AxisBase?) -> String } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/IFillFormatter.swift ================================================ // // IFillFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Protocol for providing a custom logic to where the filling line of a LineDataSet should end. This of course only works if setFillEnabled(...) is set to true. @objc(IChartFillFormatter) public protocol IFillFormatter { /// - returns: The vertical (y-axis) position where the filled-line of the LineDataSet should end. func getFillLinePosition(dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/IValueFormatter.swift ================================================ // // IValueFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation /// Interface that allows custom formatting of all values inside the chart before they are being drawn to the screen. /// /// Simply create your own formatting class and let it implement ValueFormatter. /// /// Then override the getFormattedValue(...) method and return whatever you want. @objc(IChartValueFormatter) public protocol IValueFormatter: class { /// Called when a value (from labels inside the chart) is formatted before being drawn. /// /// For performance reasons, avoid excessive calculations and memory allocations inside this method. /// /// - returns: The formatted label ready for being drawn /// /// - parameter value: The value to be formatted /// /// - parameter axis: The entry the value belongs to - in e.g. BarChart, this is of class BarEntry /// /// - parameter dataSetIndex: The index of the DataSet the entry in focus belongs to /// /// - parameter viewPortHandler: provides information about the current chart state (scale, translation, ...) /// func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String } ================================================ FILE: Pods/Charts/Source/Charts/Formatters/IndexAxisValueFormatter.swift ================================================ // // IndexAxisValueFormatter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation /// This formatter is used for passing an array of x-axis labels, on whole x steps. @objc(ChartIndexAxisValueFormatter) open class IndexAxisValueFormatter: NSObject, IAxisValueFormatter { private var _values: [String] = [String]() private var _valueCount: Int = 0 @objc public var values: [String] { get { return _values } set { _values = newValue _valueCount = _values.count } } public override init() { super.init() } @objc public init(values: [String]) { super.init() self.values = values } @objc public static func with(values: [String]) -> IndexAxisValueFormatter? { return IndexAxisValueFormatter(values: values) } open func stringForValue(_ value: Double, axis: AxisBase?) -> String { let index = Int(value.rounded()) guard values.indices.contains(index), index == Int(value) else { return "" } return _values[index] } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/BarHighlighter.swift ================================================ // // BarHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(BarChartHighlighter) open class BarHighlighter: ChartHighlighter { open override func getHighlight(x: CGFloat, y: CGFloat) -> Highlight? { guard let barData = (self.chart as? BarChartDataProvider)?.barData, let high = super.getHighlight(x: x, y: y) else { return nil } let pos = getValsForTouch(x: x, y: y) if let set = barData.getDataSetByIndex(high.dataSetIndex) as? IBarChartDataSet, set.isStacked { return getStackedHighlight(high: high, set: set, xValue: Double(pos.x), yValue: Double(pos.y)) } else { return high } } internal override func getDistance(x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat) -> CGFloat { return abs(x1 - x2) } internal override var data: ChartData? { return (chart as? BarChartDataProvider)?.barData } /// This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. /// - parameter high: the Highlight to work with looking for stacked values /// - parameter set: /// - parameter xIndex: /// - parameter yValue: /// - returns: @objc open func getStackedHighlight(high: Highlight, set: IBarChartDataSet, xValue: Double, yValue: Double) -> Highlight? { guard let chart = self.chart as? BarLineScatterCandleBubbleChartDataProvider, let entry = set.entryForXValue(xValue, closestToY: yValue) as? BarChartDataEntry else { return nil } // Not stacked if entry.yValues == nil { return high } guard let ranges = entry.ranges, ranges.count > 0 else { return nil } let stackIndex = getClosestStackIndex(ranges: ranges, value: yValue) let pixel = chart .getTransformer(forAxis: set.axisDependency) .pixelForValues(x: high.x, y: ranges[stackIndex].to) return Highlight(x: entry.x, y: entry.y, xPx: pixel.x, yPx: pixel.y, dataSetIndex: high.dataSetIndex, stackIndex: stackIndex, axis: high.axis) } /// - returns: The index of the closest value inside the values array / ranges (stacked barchart) to the value given as a parameter. /// - parameter entry: /// - parameter value: /// - returns: @objc open func getClosestStackIndex(ranges: [Range]?, value: Double) -> Int { guard let ranges = ranges else { return 0 } var stackIndex = 0 for range in ranges { if range.contains(value) { return stackIndex } else { stackIndex += 1 } } let length = max(ranges.count - 1, 0) return (value > ranges[length].to) ? length : 0 } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/ChartHighlighter.swift ================================================ // // ChartHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ChartHighlighter : NSObject, IHighlighter { /// instance of the data-provider @objc open weak var chart: ChartDataProvider? @objc public init(chart: ChartDataProvider) { self.chart = chart } open func getHighlight(x: CGFloat, y: CGFloat) -> Highlight? { let xVal = Double(getValsForTouch(x: x, y: y).x) return getHighlight(xValue: xVal, x: x, y: y) } /// - returns: The corresponding x-pos for a given touch-position in pixels. /// - parameter x: /// - returns: @objc open func getValsForTouch(x: CGFloat, y: CGFloat) -> CGPoint { guard let chart = self.chart as? BarLineScatterCandleBubbleChartDataProvider else { return .zero } // take any transformer to determine the values return chart.getTransformer(forAxis: .left).valueForTouchPoint(x: x, y: y) } /// - returns: The corresponding ChartHighlight for a given x-value and xy-touch position in pixels. /// - parameter xValue: /// - parameter x: /// - parameter y: /// - returns: @objc open func getHighlight(xValue xVal: Double, x: CGFloat, y: CGFloat) -> Highlight? { guard let chart = chart else { return nil } let closestValues = getHighlights(xValue: xVal, x: x, y: y) guard !closestValues.isEmpty else { return nil } let leftAxisMinDist = getMinimumDistance(closestValues: closestValues, y: y, axis: .left) let rightAxisMinDist = getMinimumDistance(closestValues: closestValues, y: y, axis: .right) let axis: YAxis.AxisDependency = leftAxisMinDist < rightAxisMinDist ? .left : .right let detail = closestSelectionDetailByPixel(closestValues: closestValues, x: x, y: y, axis: axis, minSelectionDistance: chart.maxHighlightDistance) return detail } /// - returns: A list of Highlight objects representing the entries closest to the given xVal. /// The returned list contains two objects per DataSet (closest rounding up, closest rounding down). /// - parameter xValue: the transformed x-value of the x-touch position /// - parameter x: touch position /// - parameter y: touch position /// - returns: @objc open func getHighlights(xValue: Double, x: CGFloat, y: CGFloat) -> [Highlight] { var vals = [Highlight]() guard let data = self.data else { return vals } for i in 0 ..< data.dataSetCount { guard let dataSet = data.getDataSetByIndex(i), dataSet.isHighlightEnabled // don't include datasets that cannot be highlighted else { continue } // extract all y-values from all DataSets at the given x-value. // some datasets (i.e bubble charts) make sense to have multiple values for an x-value. We'll have to find a way to handle that later on. It's more complicated now when x-indices are floating point. vals.append(contentsOf: buildHighlights(dataSet: dataSet, dataSetIndex: i, xValue: xValue, rounding: .closest)) } return vals } /// - returns: An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex. internal func buildHighlights( dataSet set: IChartDataSet, dataSetIndex: Int, xValue: Double, rounding: ChartDataSetRounding) -> [Highlight] { var highlights = [Highlight]() guard let chart = self.chart as? BarLineScatterCandleBubbleChartDataProvider else { return highlights } var entries = set.entriesForXValue(xValue) if entries.count == 0, let closest = set.entryForXValue(xValue, closestToY: .nan, rounding: rounding) { // Try to find closest x-value and take all entries for that x-value entries = set.entriesForXValue(closest.x) } for e in entries { let px = chart.getTransformer(forAxis: set.axisDependency).pixelForValues(x: e.x, y: e.y) let highlight = Highlight(x: e.x, y: e.y, xPx: px.x, yPx: px.y, dataSetIndex: dataSetIndex, axis: set.axisDependency) highlights.append(highlight) } return highlights } // - MARK: - Utilities /// - returns: The `ChartHighlight` of the closest value on the x-y cartesian axes internal func closestSelectionDetailByPixel( closestValues: [Highlight], x: CGFloat, y: CGFloat, axis: YAxis.AxisDependency?, minSelectionDistance: CGFloat) -> Highlight? { var distance = minSelectionDistance var closest: Highlight? for high in closestValues { if axis == nil || high.axis == axis { let cDistance = getDistance(x1: x, y1: y, x2: high.xPx, y2: high.yPx) if cDistance < distance { closest = high distance = cDistance } } } return closest } /// - returns: The minimum distance from a touch-y-value (in pixels) to the closest y-value (in pixels) that is displayed in the chart. internal func getMinimumDistance( closestValues: [Highlight], y: CGFloat, axis: YAxis.AxisDependency) -> CGFloat { var distance = CGFloat.greatestFiniteMagnitude for high in closestValues { if high.axis == axis { let tempDistance = abs(getHighlightPos(high: high) - y) if tempDistance < distance { distance = tempDistance } } } return distance } internal func getHighlightPos(high: Highlight) -> CGFloat { return high.yPx } internal func getDistance(x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat) -> CGFloat { return hypot(x1 - x2, y1 - y2) } internal var data: ChartData? { return chart?.data } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/CombinedHighlighter.swift ================================================ // // CombinedHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(CombinedChartHighlighter) open class CombinedHighlighter: ChartHighlighter { /// bar highlighter for supporting stacked highlighting private var barHighlighter: BarHighlighter? @objc public init(chart: CombinedChartDataProvider, barDataProvider: BarChartDataProvider) { super.init(chart: chart) // if there is BarData, create a BarHighlighter self.barHighlighter = barDataProvider.barData == nil ? nil : BarHighlighter(chart: barDataProvider) } open override func getHighlights(xValue: Double, x: CGFloat, y: CGFloat) -> [Highlight] { var vals = [Highlight]() guard let chart = self.chart as? CombinedChartDataProvider, let dataObjects = chart.combinedData?.allData else { return vals } for i in 0..= 0 } /// Sets the x- and y-position (pixels) where this highlight was last drawn. @objc open func setDraw(x: CGFloat, y: CGFloat) { self.drawX = x self.drawY = y } /// Sets the x- and y-position (pixels) where this highlight was last drawn. @objc open func setDraw(pt: CGPoint) { self.drawX = pt.x self.drawY = pt.y } // MARK: NSObject open override var description: String { return "Highlight, x: \(_x), y: \(_y), dataIndex (combined charts): \(dataIndex), dataSetIndex: \(_dataSetIndex), stackIndex (only stacked barentry): \(_stackIndex)" } } // MARK: Equatable extension Highlight /*: Equatable*/ { open override func isEqual(_ object: Any?) -> Bool { guard let object = object as? Highlight else { return false } if self === object { return true } return _x == object._x && _y == object._y && dataIndex == object.dataIndex && _dataSetIndex == object._dataSetIndex && _stackIndex == object._stackIndex } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/HorizontalBarHighlighter.swift ================================================ // // HorizontalBarHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(HorizontalBarChartHighlighter) open class HorizontalBarHighlighter: BarHighlighter { open override func getHighlight(x: CGFloat, y: CGFloat) -> Highlight? { guard let barData = self.chart?.data as? BarChartData else { return nil } let pos = getValsForTouch(x: y, y: x) guard let high = getHighlight(xValue: Double(pos.y), x: y, y: x) else { return nil } if let set = barData.getDataSetByIndex(high.dataSetIndex) as? IBarChartDataSet, set.isStacked { return getStackedHighlight(high: high, set: set, xValue: Double(pos.y), yValue: Double(pos.x)) } return high } internal override func buildHighlights( dataSet set: IChartDataSet, dataSetIndex: Int, xValue: Double, rounding: ChartDataSetRounding) -> [Highlight] { var highlights = [Highlight]() guard let chart = self.chart as? BarLineScatterCandleBubbleChartDataProvider else { return highlights } var entries = set.entriesForXValue(xValue) if entries.count == 0, let closest = set.entryForXValue(xValue, closestToY: .nan, rounding: rounding) { // Try to find closest x-value and take all entries for that x-value entries = set.entriesForXValue(closest.x) } for e in entries { let px = chart.getTransformer(forAxis: set.axisDependency).pixelForValues(x: e.y, y: e.x) highlights.append(Highlight(x: e.x, y: e.y, xPx: px.x, yPx: px.y, dataSetIndex: dataSetIndex, axis: set.axisDependency)) } return highlights } internal override func getDistance(x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat) -> CGFloat { return abs(y1 - y2) } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/IHighlighter.swift ================================================ // // IHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(IChartHighlighter) public protocol IHighlighter: class { /// - returns: A Highlight object corresponding to the given x- and y- touch positions in pixels. /// - parameter x: /// - parameter y: /// - returns: func getHighlight(x: CGFloat, y: CGFloat) -> Highlight? } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/PieHighlighter.swift ================================================ // // PieHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(PieChartHighlighter) open class PieHighlighter: PieRadarHighlighter { open override func closestHighlight(index: Int, x: CGFloat, y: CGFloat) -> Highlight? { guard let set = chart?.data?.dataSets[0], let entry = set.entryForIndex(index) else { return nil } return Highlight(x: Double(index), y: entry.y, xPx: x, yPx: y, dataSetIndex: 0, axis: set.axisDependency) } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/PieRadarHighlighter.swift ================================================ // // PieRadarHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(PieRadarChartHighlighter) open class PieRadarHighlighter: ChartHighlighter { open override func getHighlight(x: CGFloat, y: CGFloat) -> Highlight? { guard let chart = self.chart as? PieRadarChartViewBase else { return nil } let touchDistanceToCenter = chart.distanceToCenter(x: x, y: y) // check if a slice was touched guard touchDistanceToCenter <= chart.radius else { // if no slice was touched, highlight nothing return nil } var angle = chart.angleForPoint(x: x ,y: y) if chart is PieChartView { angle /= CGFloat(chart.chartAnimator.phaseY) } let index = chart.indexForAngle(angle) // check if the index could be found if index < 0 || index >= chart.data?.maxEntryCountSet?.entryCount ?? 0 { return nil } else { return closestHighlight(index: index, x: x, y: y) } } /// - returns: The closest Highlight object of the given objects based on the touch position inside the chart. /// - parameter index: /// - parameter x: /// - parameter y: @objc open func closestHighlight(index: Int, x: CGFloat, y: CGFloat) -> Highlight? { fatalError("closestHighlight(index, x, y) cannot be called on PieRadarChartHighlighter") } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/RadarHighlighter.swift ================================================ // // RadarHighlighter.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(RadarChartHighlighter) open class RadarHighlighter: PieRadarHighlighter { open override func closestHighlight(index: Int, x: CGFloat, y: CGFloat) -> Highlight? { guard let chart = self.chart as? RadarChartView else { return nil } let highlights = getHighlights(forIndex: index) let distanceToCenter = Double(chart.distanceToCenter(x: x, y: y) / chart.factor) var closest: Highlight? var distance = Double.greatestFiniteMagnitude for high in highlights { let cdistance = abs(high.y - distanceToCenter) if cdistance < distance { closest = high distance = cdistance } } return closest } /// - returns: An array of Highlight objects for the given index. /// The Highlight objects give information about the value at the selected index and DataSet it belongs to. /// /// - parameter index: internal func getHighlights(forIndex index: Int) -> [Highlight] { var vals = [Highlight]() guard let chart = self.chart as? RadarChartView, let chartData = chart.data else { return vals } let phaseX = chart.chartAnimator.phaseX let phaseY = chart.chartAnimator.phaseY let sliceangle = chart.sliceAngle let factor = chart.factor for i in chartData.dataSets.indices { guard let dataSet = chartData.getDataSetByIndex(i), let entry = dataSet.entryForIndex(index) else { continue } let y = (entry.y - chart.chartYMin) let p = chart.centerOffsets.moving(distance: CGFloat(y) * factor * CGFloat(phaseY), atAngle: sliceangle * CGFloat(index) * CGFloat(phaseX) + chart.rotationAngle) let highlight = Highlight(x: Double(index), y: entry.y, xPx: p.x, yPx: p.y, dataSetIndex: i, axis: dataSet.axisDependency) vals.append(highlight) } return vals } } ================================================ FILE: Pods/Charts/Source/Charts/Highlight/Range.swift ================================================ // // Range.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation @objc(ChartRange) open class Range: NSObject { @objc open var from: Double @objc open var to: Double @objc public init(from: Double, to: Double) { self.from = from self.to = to super.init() } /// - returns: `true` if this range contains (if the value is in between) the given value, `false` ifnot. /// - parameter value: @objc open func contains(_ value: Double) -> Bool { if value > from && value <= to { return true } else { return false } } @objc open func isLarger(_ value: Double) -> Bool { return value > to } @objc open func isSmaller(_ value: Double) -> Bool { return value < from } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/BarChartDataProvider.swift ================================================ // // BarChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol BarChartDataProvider: BarLineScatterCandleBubbleChartDataProvider { var barData: BarChartData? { get } var isDrawBarShadowEnabled: Bool { get } var isDrawValueAboveBarEnabled: Bool { get } var isHighlightFullBarEnabled: Bool { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift ================================================ // // BarLineScatterCandleBubbleChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol BarLineScatterCandleBubbleChartDataProvider: ChartDataProvider { func getTransformer(forAxis: YAxis.AxisDependency) -> Transformer func isInverted(axis: YAxis.AxisDependency) -> Bool var lowestVisibleX: Double { get } var highestVisibleX: Double { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/BubbleChartDataProvider.swift ================================================ // // BubbleChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol BubbleChartDataProvider: BarLineScatterCandleBubbleChartDataProvider { var bubbleData: BubbleChartData? { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/CandleChartDataProvider.swift ================================================ // // CandleChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol CandleChartDataProvider: BarLineScatterCandleBubbleChartDataProvider { var candleData: CandleChartData? { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/ChartDataProvider.swift ================================================ // // ChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol ChartDataProvider { /// - returns: The minimum x-value of the chart, regardless of zoom or translation. var chartXMin: Double { get } /// - returns: The maximum x-value of the chart, regardless of zoom or translation. var chartXMax: Double { get } /// - returns: The minimum y-value of the chart, regardless of zoom or translation. var chartYMin: Double { get } /// - returns: The maximum y-value of the chart, regardless of zoom or translation. var chartYMax: Double { get } var maxHighlightDistance: CGFloat { get } var xRange: Double { get } var centerOffsets: CGPoint { get } var data: ChartData? { get } var maxVisibleCount: Int { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/CombinedChartDataProvider.swift ================================================ // // CombinedChartDataProvider.swoft // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol CombinedChartDataProvider: LineChartDataProvider, BarChartDataProvider, BubbleChartDataProvider, CandleChartDataProvider, ScatterChartDataProvider { var combinedData: CombinedChartData? { get } } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/LineChartDataProvider.swift ================================================ // // LineChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol LineChartDataProvider: BarLineScatterCandleBubbleChartDataProvider { var lineData: LineChartData? { get } func getAxis(_ axis: YAxis.AxisDependency) -> YAxis } ================================================ FILE: Pods/Charts/Source/Charts/Interfaces/ScatterChartDataProvider.swift ================================================ // // ScatterChartDataProvider.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol ScatterChartDataProvider: BarLineScatterCandleBubbleChartDataProvider { var scatterData: ScatterChartData? { get } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/AnimatedMoveViewJob.swift ================================================ // // AnimatedMoveViewJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class AnimatedMoveViewJob: AnimatedViewPortJob { internal override func animationUpdate() { guard let viewPortHandler = viewPortHandler, let transformer = transformer, let view = view else { return } var pt = CGPoint( x: xOrigin + (CGFloat(xValue) - xOrigin) * phase, y: yOrigin + (CGFloat(yValue) - yOrigin) * phase ) transformer.pointValueToPixel(&pt) viewPortHandler.centerViewPort(pt: pt, chart: view) } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/AnimatedViewPortJob.swift ================================================ // // AnimatedViewPortJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class AnimatedViewPortJob: ViewPortJob { internal var phase: CGFloat = 1.0 internal var xOrigin: CGFloat = 0.0 internal var yOrigin: CGFloat = 0.0 private var _startTime: TimeInterval = 0.0 private var _displayLink: NSUIDisplayLink! private var _duration: TimeInterval = 0.0 private var _endTime: TimeInterval = 0.0 private var _easing: ChartEasingFunctionBlock? @objc public init( viewPortHandler: ViewPortHandler, xValue: Double, yValue: Double, transformer: Transformer, view: ChartViewBase, xOrigin: CGFloat, yOrigin: CGFloat, duration: TimeInterval, easing: ChartEasingFunctionBlock?) { super.init(viewPortHandler: viewPortHandler, xValue: xValue, yValue: yValue, transformer: transformer, view: view) self.xOrigin = xOrigin self.yOrigin = yOrigin self._duration = duration self._easing = easing } deinit { stop(finish: false) } open override func doJob() { start() } @objc open func start() { _startTime = CACurrentMediaTime() _endTime = _startTime + _duration _endTime = _endTime > _endTime ? _endTime : _endTime updateAnimationPhase(_startTime) _displayLink = NSUIDisplayLink(target: self, selector: #selector(animationLoop)) _displayLink.add(to: .main, forMode: .common) } @objc open func stop(finish: Bool) { guard _displayLink != nil else { return } _displayLink.remove(from: .main, forMode: .common) _displayLink = nil if finish { if phase != 1.0 { phase = 1.0 animationUpdate() } animationEnd() } } private func updateAnimationPhase(_ currentTime: TimeInterval) { let elapsedTime = currentTime - _startTime let duration = _duration var elapsed = elapsedTime elapsed = min(elapsed, duration) phase = CGFloat(_easing?(elapsed, duration) ?? elapsed / duration) } @objc private func animationLoop() { let currentTime: TimeInterval = CACurrentMediaTime() updateAnimationPhase(currentTime) animationUpdate() if currentTime >= _endTime { stop(finish: true) } } internal func animationUpdate() { // Override this } internal func animationEnd() { // Override this } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/AnimatedZoomViewJob.swift ================================================ // // AnimatedZoomViewJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class AnimatedZoomViewJob: AnimatedViewPortJob { internal var yAxis: YAxis? internal var xAxisRange: Double = 0.0 internal var scaleX: CGFloat = 0.0 internal var scaleY: CGFloat = 0.0 internal var zoomOriginX: CGFloat = 0.0 internal var zoomOriginY: CGFloat = 0.0 internal var zoomCenterX: CGFloat = 0.0 internal var zoomCenterY: CGFloat = 0.0 @objc public init( viewPortHandler: ViewPortHandler, transformer: Transformer, view: ChartViewBase, yAxis: YAxis, xAxisRange: Double, scaleX: CGFloat, scaleY: CGFloat, xOrigin: CGFloat, yOrigin: CGFloat, zoomCenterX: CGFloat, zoomCenterY: CGFloat, zoomOriginX: CGFloat, zoomOriginY: CGFloat, duration: TimeInterval, easing: ChartEasingFunctionBlock?) { super.init(viewPortHandler: viewPortHandler, xValue: 0.0, yValue: 0.0, transformer: transformer, view: view, xOrigin: xOrigin, yOrigin: yOrigin, duration: duration, easing: easing) self.yAxis = yAxis self.xAxisRange = xAxisRange self.scaleX = scaleX self.scaleY = scaleY self.zoomCenterX = zoomCenterX self.zoomCenterY = zoomCenterY self.zoomOriginX = zoomOriginX self.zoomOriginY = zoomOriginY } internal override func animationUpdate() { guard let viewPortHandler = viewPortHandler, let transformer = transformer, let view = view else { return } let scaleX = xOrigin + (self.scaleX - xOrigin) * phase let scaleY = yOrigin + (self.scaleY - yOrigin) * phase var matrix = viewPortHandler.setZoom(scaleX: scaleX, scaleY: scaleY) viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) let valsInView = CGFloat(yAxis?.axisRange ?? 0.0) / viewPortHandler.scaleY let xsInView = CGFloat(xAxisRange) / viewPortHandler.scaleX var pt = CGPoint( x: zoomOriginX + ((zoomCenterX - xsInView / 2.0) - zoomOriginX) * phase, y: zoomOriginY + ((zoomCenterY + valsInView / 2.0) - zoomOriginY) * phase ) transformer.pointValueToPixel(&pt) matrix = viewPortHandler.translate(pt: pt) viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: true) } internal override func animationEnd() { (view as? BarLineChartViewBase)?.calculateOffsets() view?.setNeedsDisplay() } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/MoveViewJob.swift ================================================ // // MoveViewJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(MoveChartViewJob) open class MoveViewJob: ViewPortJob { open override func doJob() { guard let viewPortHandler = viewPortHandler, let transformer = transformer, let view = view else { return } var pt = CGPoint( x: xValue, y: yValue ) transformer.pointValueToPixel(&pt) viewPortHandler.centerViewPort(pt: pt, chart: view) } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/ViewPortJob.swift ================================================ // // ViewPortJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics // This defines a viewport modification job, used for delaying or animating viewport changes @objc(ChartViewPortJob) open class ViewPortJob: NSObject { internal var point: CGPoint = CGPoint() internal weak var viewPortHandler: ViewPortHandler? internal var xValue: Double = 0.0 internal var yValue: Double = 0.0 internal weak var transformer: Transformer? internal weak var view: ChartViewBase? @objc public init( viewPortHandler: ViewPortHandler, xValue: Double, yValue: Double, transformer: Transformer, view: ChartViewBase) { super.init() self.viewPortHandler = viewPortHandler self.xValue = xValue self.yValue = yValue self.transformer = transformer self.view = view } @objc open func doJob() { fatalError("`doJob()` must be overridden by subclasses") } } ================================================ FILE: Pods/Charts/Source/Charts/Jobs/ZoomViewJob.swift ================================================ // // ZoomViewJob.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ZoomChartViewJob) open class ZoomViewJob: ViewPortJob { internal var scaleX: CGFloat = 0.0 internal var scaleY: CGFloat = 0.0 internal var axisDependency: YAxis.AxisDependency = .left @objc public init( viewPortHandler: ViewPortHandler, scaleX: CGFloat, scaleY: CGFloat, xValue: Double, yValue: Double, transformer: Transformer, axis: YAxis.AxisDependency, view: ChartViewBase) { super.init( viewPortHandler: viewPortHandler, xValue: xValue, yValue: yValue, transformer: transformer, view: view) self.scaleX = scaleX self.scaleY = scaleY self.axisDependency = axis } open override func doJob() { guard let viewPortHandler = viewPortHandler, let transformer = transformer, let view = view else { return } var matrix = viewPortHandler.setZoom(scaleX: scaleX, scaleY: scaleY) viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) let yValsInView = (view as! BarLineChartViewBase).getAxis(axisDependency).axisRange / Double(viewPortHandler.scaleY) let xValsInView = (view as! BarLineChartViewBase).xAxis.axisRange / Double(viewPortHandler.scaleX) var pt = CGPoint( x: CGFloat(xValue - xValsInView / 2.0), y: CGFloat(yValue + yValsInView / 2.0) ) transformer.pointValueToPixel(&pt) matrix = viewPortHandler.translate(pt: pt) viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) (view as! BarLineChartViewBase).calculateOffsets() view.setNeedsDisplay() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/AxisRendererBase.swift ================================================ // // AxisRendererBase.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartAxisRendererBase) open class AxisRendererBase: Renderer { /// base axis this axis renderer works with @objc open var axis: AxisBase? /// transformer to transform values to screen pixels and return @objc open var transformer: Transformer? @objc public init(viewPortHandler: ViewPortHandler, transformer: Transformer?, axis: AxisBase?) { super.init(viewPortHandler: viewPortHandler) self.transformer = transformer self.axis = axis } /// Draws the axis labels on the specified context @objc open func renderAxisLabels(context: CGContext) { fatalError("renderAxisLabels() cannot be called on AxisRendererBase") } /// Draws the grid lines belonging to the axis. @objc open func renderGridLines(context: CGContext) { fatalError("renderGridLines() cannot be called on AxisRendererBase") } /// Draws the line that goes alongside the axis. @objc open func renderAxisLine(context: CGContext) { fatalError("renderAxisLine() cannot be called on AxisRendererBase") } /// Draws the LimitLines associated with this axis to the screen. @objc open func renderLimitLines(context: CGContext) { fatalError("renderLimitLines() cannot be called on AxisRendererBase") } /// Computes the axis values. /// - parameter min: the minimum value in the data object for this axis /// - parameter max: the maximum value in the data object for this axis @objc open func computeAxis(min: Double, max: Double, inverted: Bool) { var min = min, max = max if let transformer = self.transformer { // calculate the starting and entry point of the y-labels (depending on zoom / contentrect bounds) if viewPortHandler.contentWidth > 10.0 && !viewPortHandler.isFullyZoomedOutY { let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) if !inverted { min = Double(p2.y) max = Double(p1.y) } else { min = Double(p1.y) max = Double(p2.y) } } } computeAxisValues(min: min, max: max) } /// Sets up the axis values. Computes the desired number of labels between the two given extremes. @objc open func computeAxisValues(min: Double, max: Double) { guard let axis = self.axis else { return } let yMin = min let yMax = max let labelCount = axis.labelCount let range = abs(yMax - yMin) if labelCount == 0 || range <= 0 || range.isInfinite { axis.entries = [Double]() axis.centeredEntries = [Double]() return } // Find out how much spacing (in y value space) between axis values let rawInterval = range / Double(labelCount) var interval = rawInterval.roundedToNextSignficant() // If granularity is enabled, then do not allow the interval to go below specified granularity. // This is used to avoid repeated values when rounding values for display. if axis.granularityEnabled { interval = interval < axis.granularity ? axis.granularity : interval } // Normalize interval let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant() let intervalSigDigit = Int(interval / intervalMagnitude) if intervalSigDigit > 5 { // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 // if it's 0.0 after floor(), we use the old value interval = floor(10.0 * intervalMagnitude) == 0.0 ? interval : floor(10.0 * intervalMagnitude) } var n = axis.centerAxisLabelsEnabled ? 1 : 0 // force label count if axis.isForceLabelsEnabled { interval = Double(range) / Double(labelCount - 1) // Ensure stops contains at least n elements. axis.entries.removeAll(keepingCapacity: true) axis.entries.reserveCapacity(labelCount) var v = yMin for _ in 0 ..< labelCount { axis.entries.append(v) v += interval } n = labelCount } else { // no forced count var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval if axis.centerAxisLabelsEnabled { first -= interval } let last = interval == 0.0 ? 0.0 : (floor(yMax / interval) * interval).nextUp if interval != 0.0 && last != first { for _ in stride(from: first, through: last, by: interval) { n += 1 } } else if last == first && n == 0 { n = 1 } // Ensure stops contains at least n elements. axis.entries.removeAll(keepingCapacity: true) axis.entries.reserveCapacity(labelCount) var f = first var i = 0 while i < n { if f == 0.0 { // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) f = 0.0 } axis.entries.append(Double(f)) f += interval i += 1 } } // set decimals if interval < 1 { axis.decimals = Int(ceil(-log10(interval))) } else { axis.decimals = 0 } if axis.centerAxisLabelsEnabled { axis.centeredEntries.reserveCapacity(n) axis.centeredEntries.removeAll() let offset: Double = interval / 2.0 for i in 0 ..< n { axis.centeredEntries.append(axis.entries[i] + offset) } } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/BarChartRenderer.swift ================================================ // // BarChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class BarChartRenderer: BarLineScatterCandleBubbleRenderer { private class Buffer { var rects = [CGRect]() } @objc open weak var dataProvider: BarChartDataProvider? @objc public init(dataProvider: BarChartDataProvider, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.dataProvider = dataProvider } // [CGRect] per dataset private var _buffers = [Buffer]() open override func initBuffers() { if let barData = dataProvider?.barData { // Matche buffers count to dataset count if _buffers.count != barData.dataSetCount { while _buffers.count < barData.dataSetCount { _buffers.append(Buffer()) } while _buffers.count > barData.dataSetCount { _buffers.removeLast() } } for i in stride(from: 0, to: barData.dataSetCount, by: 1) { let set = barData.dataSets[i] as! IBarChartDataSet let size = set.entryCount * (set.isStacked ? set.stackSize : 1) if _buffers[i].rects.count != size { _buffers[i].rects = [CGRect](repeating: CGRect(), count: size) } } } else { _buffers.removeAll() } } private func prepareBuffer(dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } let barWidthHalf = barData.barWidth / 2.0 let buffer = _buffers[index] var bufferIndex = 0 let containsStacks = dataSet.isStacked let isInverted = dataProvider.isInverted(axis: dataSet.axisDependency) let phaseY = animator.phaseY var barRect = CGRect() var x: Double var y: Double for i in stride(from: 0, to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } let vals = e.yValues x = e.x y = e.y if !containsStacks || vals == nil { let left = CGFloat(x - barWidthHalf) let right = CGFloat(x + barWidthHalf) var top = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) var bottom = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) // multiply the height of the rect with the phase if top > 0 { top *= CGFloat(phaseY) } else { bottom *= CGFloat(phaseY) } barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top buffer.rects[bufferIndex] = barRect bufferIndex += 1 } else { var posY = 0.0 var negY = -e.negativeSum var yStart = 0.0 // fill the stack for k in 0 ..< vals!.count { let value = vals![k] if value == 0.0 && (posY == 0.0 || negY == 0.0) { // Take care of the situation of a 0.0 value, which overlaps a non-zero bar y = value yStart = y } else if value >= 0.0 { y = posY yStart = posY + value posY = yStart } else { y = negY yStart = negY + abs(value) negY += abs(value) } let left = CGFloat(x - barWidthHalf) let right = CGFloat(x + barWidthHalf) var top = isInverted ? (y <= yStart ? CGFloat(y) : CGFloat(yStart)) : (y >= yStart ? CGFloat(y) : CGFloat(yStart)) var bottom = isInverted ? (y >= yStart ? CGFloat(y) : CGFloat(yStart)) : (y <= yStart ? CGFloat(y) : CGFloat(yStart)) // multiply the height of the rect with the phase top *= CGFloat(phaseY) bottom *= CGFloat(phaseY) barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top buffer.rects[bufferIndex] = barRect bufferIndex += 1 } } } } open override func drawData(context: CGContext) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } for i in 0 ..< barData.dataSetCount { guard let set = barData.getDataSetByIndex(i) else { continue } if set.isVisible { if !(set is IBarChartDataSet) { fatalError("Datasets for BarChartRenderer must conform to IBarChartDataset") } drawDataSet(context: context, dataSet: set as! IBarChartDataSet, index: i) } } } private var _barShadowRectBuffer: CGRect = CGRect() @objc open func drawDataSet(context: CGContext, dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) prepareBuffer(dataSet: dataSet, index: index) trans.rectValuesToPixel(&_buffers[index].rects) let borderWidth = dataSet.barBorderWidth let borderColor = dataSet.barBorderColor let drawBorder = borderWidth > 0.0 context.saveGState() // draw the bar shadow before the values if dataProvider.isDrawBarShadowEnabled { guard let barData = dataProvider.barData else { return } let barWidth = barData.barWidth let barWidthHalf = barWidth / 2.0 var x: Double = 0.0 for i in stride(from: 0, to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } x = e.x _barShadowRectBuffer.origin.x = CGFloat(x - barWidthHalf) _barShadowRectBuffer.size.width = CGFloat(barWidth) trans.rectValueToPixel(&_barShadowRectBuffer) if !viewPortHandler.isInBoundsLeft(_barShadowRectBuffer.origin.x + _barShadowRectBuffer.size.width) { continue } if !viewPortHandler.isInBoundsRight(_barShadowRectBuffer.origin.x) { break } _barShadowRectBuffer.origin.y = viewPortHandler.contentTop _barShadowRectBuffer.size.height = viewPortHandler.contentHeight context.setFillColor(dataSet.barShadowColor.cgColor) context.fill(_barShadowRectBuffer) } } let buffer = _buffers[index] // draw the bar shadow before the values if dataProvider.isDrawBarShadowEnabled { for j in stride(from: 0, to: buffer.rects.count, by: 1) { let barRect = buffer.rects[j] if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) { continue } if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) { break } context.setFillColor(dataSet.barShadowColor.cgColor) context.fill(barRect) } } let isSingleColor = dataSet.colors.count == 1 if isSingleColor { context.setFillColor(dataSet.color(atIndex: 0).cgColor) } for j in stride(from: 0, to: buffer.rects.count, by: 1) { let barRect = buffer.rects[j] if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) { continue } if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) { break } if !isSingleColor { // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. context.setFillColor(dataSet.color(atIndex: j).cgColor) } context.fill(barRect) if drawBorder { context.setStrokeColor(borderColor.cgColor) context.setLineWidth(borderWidth) context.stroke(barRect) } } context.restoreGState() } open func prepareBarHighlight( x: Double, y1: Double, y2: Double, barWidthHalf: Double, trans: Transformer, rect: inout CGRect) { let left = x - barWidthHalf let right = x + barWidthHalf let top = y1 let bottom = y2 rect.origin.x = CGFloat(left) rect.origin.y = CGFloat(top) rect.size.width = CGFloat(right - left) rect.size.height = CGFloat(bottom - top) trans.rectValueToPixel(&rect, phaseY: animator.phaseY ) } open override func drawValues(context: CGContext) { // if values are drawn if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } var dataSets = barData.dataSets let valueOffsetPlus: CGFloat = 4.5 var posOffset: CGFloat var negOffset: CGFloat let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled for dataSetIndex in 0 ..< barData.dataSetCount { guard let dataSet = dataSets[dataSetIndex] as? IBarChartDataSet else { continue } if !shouldDrawValues(forDataSet: dataSet) { continue } let isInverted = dataProvider.isInverted(axis: dataSet.axisDependency) // calculate the correct offset depending on the draw position of the value let valueFont = dataSet.valueFont let valueTextHeight = valueFont.lineHeight posOffset = (drawValueAboveBar ? -(valueTextHeight + valueOffsetPlus) : valueOffsetPlus) negOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextHeight + valueOffsetPlus)) if isInverted { posOffset = -posOffset - valueTextHeight negOffset = -negOffset - valueTextHeight } let buffer = _buffers[dataSetIndex] guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY let iconsOffset = dataSet.iconsOffset // if only single values are drawn (sum) if !dataSet.isStacked { for j in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } let rect = buffer.rects[j] let x = rect.origin.x + rect.size.width / 2.0 if !viewPortHandler.isInBoundsRight(x) { break } if !viewPortHandler.isInBoundsY(rect.origin.y) || !viewPortHandler.isInBoundsLeft(x) { continue } let val = e.y if dataSet.isDrawValuesEnabled { drawValue( context: context, value: formatter.stringForValue( val, entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler), xPos: x, yPos: val >= 0.0 ? (rect.origin.y + posOffset) : (rect.origin.y + rect.size.height + negOffset), font: valueFont, align: .center, color: dataSet.valueTextColorAt(j)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { var px = x var py = val >= 0.0 ? (rect.origin.y + posOffset) : (rect.origin.y + rect.size.height + negOffset) px += iconsOffset.x py += iconsOffset.y ChartUtils.drawImage( context: context, image: icon, x: px, y: py, size: icon.size) } } } else { // if we have stacks var bufferIndex = 0 for index in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(index) as? BarChartDataEntry else { continue } let vals = e.yValues let rect = buffer.rects[bufferIndex] let x = rect.origin.x + rect.size.width / 2.0 // we still draw stacked bars, but there is one non-stacked in between if vals == nil { if !viewPortHandler.isInBoundsRight(x) { break } if !viewPortHandler.isInBoundsY(rect.origin.y) || !viewPortHandler.isInBoundsLeft(x) { continue } if dataSet.isDrawValuesEnabled { drawValue( context: context, value: formatter.stringForValue( e.y, entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler), xPos: x, yPos: rect.origin.y + (e.y >= 0 ? posOffset : negOffset), font: valueFont, align: .center, color: dataSet.valueTextColorAt(index)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { var px = x var py = rect.origin.y + (e.y >= 0 ? posOffset : negOffset) px += iconsOffset.x py += iconsOffset.y ChartUtils.drawImage( context: context, image: icon, x: px, y: py, size: icon.size) } } else { // draw stack values let vals = vals! var transformed = [CGPoint]() var posY = 0.0 var negY = -e.negativeSum for k in 0 ..< vals.count { let value = vals[k] var y: Double if value == 0.0 && (posY == 0.0 || negY == 0.0) { // Take care of the situation of a 0.0 value, which overlaps a non-zero bar y = value } else if value >= 0.0 { posY += value y = posY } else { y = negY negY -= value } transformed.append(CGPoint(x: 0.0, y: CGFloat(y * phaseY))) } trans.pointValuesToPixel(&transformed) for k in 0 ..< transformed.count { let val = vals[k] let drawBelow = (val == 0.0 && negY == 0.0 && posY > 0.0) || val < 0.0 let y = transformed[k].y + (drawBelow ? negOffset : posOffset) if !viewPortHandler.isInBoundsRight(x) { break } if !viewPortHandler.isInBoundsY(y) || !viewPortHandler.isInBoundsLeft(x) { continue } if dataSet.isDrawValuesEnabled { drawValue( context: context, value: formatter.stringForValue( vals[k], entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler), xPos: x, yPos: y, font: valueFont, align: .center, color: dataSet.valueTextColorAt(index)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { ChartUtils.drawImage( context: context, image: icon, x: x + iconsOffset.x, y: y + iconsOffset.y, size: icon.size) } } } bufferIndex = vals == nil ? (bufferIndex + 1) : (bufferIndex + vals!.count) } } } } } /// Draws a value at the specified x and y position. @objc open func drawValue(context: CGContext, value: String, xPos: CGFloat, yPos: CGFloat, font: NSUIFont, align: NSTextAlignment, color: NSUIColor) { ChartUtils.drawText(context: context, text: value, point: CGPoint(x: xPos, y: yPos), align: align, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: color]) } open override func drawExtras(context: CGContext) { } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } context.saveGState() var barRect = CGRect() for high in indices { guard let set = barData.getDataSetByIndex(high.dataSetIndex) as? IBarChartDataSet, set.isHighlightEnabled else { continue } if let e = set.entryForXValue(high.x, closestToY: high.y) as? BarChartDataEntry { if !isInBoundsX(entry: e, dataSet: set) { continue } let trans = dataProvider.getTransformer(forAxis: set.axisDependency) context.setFillColor(set.highlightColor.cgColor) context.setAlpha(set.highlightAlpha) let isStack = high.stackIndex >= 0 && e.isStacked let y1: Double let y2: Double if isStack { if dataProvider.isHighlightFullBarEnabled { y1 = e.positiveSum y2 = -e.negativeSum } else { let range = e.ranges?[high.stackIndex] y1 = range?.from ?? 0.0 y2 = range?.to ?? 0.0 } } else { y1 = e.y y2 = 0.0 } prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect) setHighlightDrawPos(highlight: high, barRect: barRect) context.fill(barRect) } } context.restoreGState() } /// Sets the drawing position of the highlight object based on the riven bar-rect. internal func setHighlightDrawPos(highlight high: Highlight, barRect: CGRect) { high.setDraw(x: barRect.midX, y: barRect.origin.y) } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift ================================================ // // BarLineScatterCandleBubbleRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(BarLineScatterCandleBubbleChartRenderer) open class BarLineScatterCandleBubbleRenderer: DataRenderer { internal var _xBounds = XBounds() // Reusable XBounds object public override init(animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) } /// Checks if the provided entry object is in bounds for drawing considering the current animation phase. internal func isInBoundsX(entry e: ChartDataEntry, dataSet: IBarLineScatterCandleBubbleChartDataSet) -> Bool { let entryIndex = dataSet.entryIndex(entry: e) return Double(entryIndex) < Double(dataSet.entryCount) * animator.phaseX } /// Calculates and returns the x-bounds for the given DataSet in terms of index in their values array. /// This includes minimum and maximum visible x, as well as range. internal func xBounds(chart: BarLineScatterCandleBubbleChartDataProvider, dataSet: IBarLineScatterCandleBubbleChartDataSet, animator: Animator?) -> XBounds { return XBounds(chart: chart, dataSet: dataSet, animator: animator) } /// - returns: `true` if the DataSet values should be drawn, `false` if not. internal func shouldDrawValues(forDataSet set: IChartDataSet) -> Bool { return set.isVisible && (set.isDrawValuesEnabled || set.isDrawIconsEnabled) } /// Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. open class XBounds { /// minimum visible entry index open var min: Int = 0 /// maximum visible entry index open var max: Int = 0 /// range of visible entry indices open var range: Int = 0 public init() { } public init(chart: BarLineScatterCandleBubbleChartDataProvider, dataSet: IBarLineScatterCandleBubbleChartDataSet, animator: Animator?) { self.set(chart: chart, dataSet: dataSet, animator: animator) } /// Calculates the minimum and maximum x values as well as the range between them. open func set(chart: BarLineScatterCandleBubbleChartDataProvider, dataSet: IBarLineScatterCandleBubbleChartDataSet, animator: Animator?) { let phaseX = Swift.max(0.0, Swift.min(1.0, animator?.phaseX ?? 1.0)) let low = chart.lowestVisibleX let high = chart.highestVisibleX let entryFrom = dataSet.entryForXValue(low, closestToY: Double.nan, rounding: ChartDataSetRounding.down) let entryTo = dataSet.entryForXValue(high, closestToY: Double.nan, rounding: ChartDataSetRounding.up) self.min = entryFrom == nil ? 0 : dataSet.entryIndex(entry: entryFrom!) self.max = entryTo == nil ? 0 : dataSet.entryIndex(entry: entryTo!) range = Int(Double(self.max - self.min) * phaseX) } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/BubbleChartRenderer.swift ================================================ // // BubbleChartRenderer.swift // Charts // // Bubble chart implementation: // Copyright 2015 Pierre-Marc Airoldi // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class BubbleChartRenderer: BarLineScatterCandleBubbleRenderer { @objc open weak var dataProvider: BubbleChartDataProvider? @objc public init(dataProvider: BubbleChartDataProvider, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.dataProvider = dataProvider } open override func drawData(context: CGContext) { guard let dataProvider = dataProvider, let bubbleData = dataProvider.bubbleData else { return } for set in bubbleData.dataSets as! [IBubbleChartDataSet] where set.isVisible { drawDataSet(context: context, dataSet: set) } } private func getShapeSize( entrySize: CGFloat, maxSize: CGFloat, reference: CGFloat, normalizeSize: Bool) -> CGFloat { let factor: CGFloat = normalizeSize ? ((maxSize == 0.0) ? 1.0 : sqrt(entrySize / maxSize)) : entrySize let shapeSize: CGFloat = reference * factor return shapeSize } private var _pointBuffer = CGPoint() private var _sizeBuffer = [CGPoint](repeating: CGPoint(), count: 2) @objc open func drawDataSet(context: CGContext, dataSet: IBubbleChartDataSet) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) let valueToPixelMatrix = trans.valueToPixelMatrix _sizeBuffer[0].x = 0.0 _sizeBuffer[0].y = 0.0 _sizeBuffer[1].x = 1.0 _sizeBuffer[1].y = 0.0 trans.pointValuesToPixel(&_sizeBuffer) context.saveGState() defer { context.restoreGState() } let normalizeSize = dataSet.isNormalizeSizeEnabled // calcualte the full width of 1 step on the x-axis let maxBubbleWidth: CGFloat = abs(_sizeBuffer[1].x - _sizeBuffer[0].x) let maxBubbleHeight: CGFloat = abs(viewPortHandler.contentBottom - viewPortHandler.contentTop) let referenceSize: CGFloat = min(maxBubbleHeight, maxBubbleWidth) for j in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { guard let entry = dataSet.entryForIndex(j) as? BubbleChartDataEntry else { continue } _pointBuffer.x = CGFloat(entry.x) _pointBuffer.y = CGFloat(entry.y * phaseY) _pointBuffer = _pointBuffer.applying(valueToPixelMatrix) let shapeSize = getShapeSize(entrySize: entry.size, maxSize: dataSet.maxSize, reference: referenceSize, normalizeSize: normalizeSize) let shapeHalf = shapeSize / 2.0 guard viewPortHandler.isInBoundsTop(_pointBuffer.y + shapeHalf), viewPortHandler.isInBoundsBottom(_pointBuffer.y - shapeHalf), viewPortHandler.isInBoundsLeft(_pointBuffer.x + shapeHalf) else { continue } guard viewPortHandler.isInBoundsRight(_pointBuffer.x - shapeHalf) else { break } let color = dataSet.color(atIndex: j) let rect = CGRect( x: _pointBuffer.x - shapeHalf, y: _pointBuffer.y - shapeHalf, width: shapeSize, height: shapeSize ) context.setFillColor(color.cgColor) context.fillEllipse(in: rect) } } open override func drawValues(context: CGContext) { guard let dataProvider = dataProvider, let bubbleData = dataProvider.bubbleData, isDrawingValuesAllowed(dataProvider: dataProvider), let dataSets = bubbleData.dataSets as? [IBubbleChartDataSet] else { return } let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY var pt = CGPoint() for i in 0.. close { _shadowPoints[0].y = CGFloat(high * phaseY) _shadowPoints[1].y = CGFloat(open * phaseY) _shadowPoints[2].y = CGFloat(low * phaseY) _shadowPoints[3].y = CGFloat(close * phaseY) } else if open < close { _shadowPoints[0].y = CGFloat(high * phaseY) _shadowPoints[1].y = CGFloat(close * phaseY) _shadowPoints[2].y = CGFloat(low * phaseY) _shadowPoints[3].y = CGFloat(open * phaseY) } else { _shadowPoints[0].y = CGFloat(high * phaseY) _shadowPoints[1].y = CGFloat(open * phaseY) _shadowPoints[2].y = CGFloat(low * phaseY) _shadowPoints[3].y = _shadowPoints[1].y } trans.pointValuesToPixel(&_shadowPoints) // draw the shadows var shadowColor: NSUIColor! = nil if dataSet.shadowColorSameAsCandle { if open > close { shadowColor = dataSet.decreasingColor ?? dataSet.color(atIndex: j) } else if open < close { shadowColor = dataSet.increasingColor ?? dataSet.color(atIndex: j) } else { shadowColor = dataSet.neutralColor ?? dataSet.color(atIndex: j) } } if shadowColor === nil { shadowColor = dataSet.shadowColor ?? dataSet.color(atIndex: j) } context.setStrokeColor(shadowColor.cgColor) context.strokeLineSegments(between: _shadowPoints) // calculate the body _bodyRect.origin.x = CGFloat(xPos) - 0.5 + barSpace _bodyRect.origin.y = CGFloat(close * phaseY) _bodyRect.size.width = (CGFloat(xPos) + 0.5 - barSpace) - _bodyRect.origin.x _bodyRect.size.height = CGFloat(open * phaseY) - _bodyRect.origin.y trans.rectValueToPixel(&_bodyRect) // draw body differently for increasing and decreasing entry if open > close { let color = dataSet.decreasingColor ?? dataSet.color(atIndex: j) if dataSet.isDecreasingFilled { context.setFillColor(color.cgColor) context.fill(_bodyRect) } else { context.setStrokeColor(color.cgColor) context.stroke(_bodyRect) } } else if open < close { let color = dataSet.increasingColor ?? dataSet.color(atIndex: j) if dataSet.isIncreasingFilled { context.setFillColor(color.cgColor) context.fill(_bodyRect) } else { context.setStrokeColor(color.cgColor) context.stroke(_bodyRect) } } else { let color = dataSet.neutralColor ?? dataSet.color(atIndex: j) context.setStrokeColor(color.cgColor) context.stroke(_bodyRect) } } else { _rangePoints[0].x = CGFloat(xPos) _rangePoints[0].y = CGFloat(high * phaseY) _rangePoints[1].x = CGFloat(xPos) _rangePoints[1].y = CGFloat(low * phaseY) _openPoints[0].x = CGFloat(xPos) - 0.5 + barSpace _openPoints[0].y = CGFloat(open * phaseY) _openPoints[1].x = CGFloat(xPos) _openPoints[1].y = CGFloat(open * phaseY) _closePoints[0].x = CGFloat(xPos) + 0.5 - barSpace _closePoints[0].y = CGFloat(close * phaseY) _closePoints[1].x = CGFloat(xPos) _closePoints[1].y = CGFloat(close * phaseY) trans.pointValuesToPixel(&_rangePoints) trans.pointValuesToPixel(&_openPoints) trans.pointValuesToPixel(&_closePoints) // draw the ranges var barColor: NSUIColor! = nil if open > close { barColor = dataSet.decreasingColor ?? dataSet.color(atIndex: j) } else if open < close { barColor = dataSet.increasingColor ?? dataSet.color(atIndex: j) } else { barColor = dataSet.neutralColor ?? dataSet.color(atIndex: j) } context.setStrokeColor(barColor.cgColor) context.strokeLineSegments(between: _rangePoints) context.strokeLineSegments(between: _openPoints) context.strokeLineSegments(between: _closePoints) } } context.restoreGState() } open override func drawValues(context: CGContext) { guard let dataProvider = dataProvider, let candleData = dataProvider.candleData else { return } // if values are drawn if isDrawingValuesAllowed(dataProvider: dataProvider) { var dataSets = candleData.dataSets let phaseY = animator.phaseY var pt = CGPoint() for i in 0 ..< dataSets.count { guard let dataSet = dataSets[i] as? IBarLineScatterCandleBubbleChartDataSet else { continue } if !shouldDrawValues(forDataSet: dataSet) { continue } let valueFont = dataSet.valueFont guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix let iconsOffset = dataSet.iconsOffset _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) let lineHeight = valueFont.lineHeight let yOffset: CGFloat = lineHeight + 5.0 for j in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { guard let e = dataSet.entryForIndex(j) as? CandleChartDataEntry else { break } pt.x = CGFloat(e.x) pt.y = CGFloat(e.high * phaseY) pt = pt.applying(valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) { break } if (!viewPortHandler.isInBoundsLeft(pt.x) || !viewPortHandler.isInBoundsY(pt.y)) { continue } if dataSet.isDrawValuesEnabled { ChartUtils.drawText( context: context, text: formatter.stringForValue( e.high, entry: e, dataSetIndex: i, viewPortHandler: viewPortHandler), point: CGPoint( x: pt.x, y: pt.y - yOffset), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: dataSet.valueTextColorAt(j)]) } if let icon = e.icon, dataSet.isDrawIconsEnabled { ChartUtils.drawImage(context: context, image: icon, x: pt.x + iconsOffset.x, y: pt.y + iconsOffset.y, size: icon.size) } } } } } open override func drawExtras(context: CGContext) { } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let dataProvider = dataProvider, let candleData = dataProvider.candleData else { return } context.saveGState() for high in indices { guard let set = candleData.getDataSetByIndex(high.dataSetIndex) as? ICandleChartDataSet, set.isHighlightEnabled else { continue } guard let e = set.entryForXValue(high.x, closestToY: high.y) as? CandleChartDataEntry else { continue } if !isInBoundsX(entry: e, dataSet: set) { continue } let trans = dataProvider.getTransformer(forAxis: set.axisDependency) context.setStrokeColor(set.highlightColor.cgColor) context.setLineWidth(set.highlightLineWidth) if set.highlightLineDashLengths != nil { context.setLineDash(phase: set.highlightLineDashPhase, lengths: set.highlightLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } let lowValue = e.low * Double(animator.phaseY) let highValue = e.high * Double(animator.phaseY) let y = (lowValue + highValue) / 2.0 let pt = trans.pixelForValues(x: e.x, y: y) high.setDraw(pt: pt) // draw the lines drawHighlightLines(context: context, point: pt, set: set) } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/ChartDataRendererBase.swift ================================================ // // DataRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartDataRendererBase) open class DataRenderer: Renderer { @objc public let animator: Animator @objc public init(animator: Animator, viewPortHandler: ViewPortHandler) { self.animator = animator super.init(viewPortHandler: viewPortHandler) } @objc open func drawData(context: CGContext) { fatalError("drawData() cannot be called on DataRenderer") } @objc open func drawValues(context: CGContext) { fatalError("drawValues() cannot be called on DataRenderer") } @objc open func drawExtras(context: CGContext) { fatalError("drawExtras() cannot be called on DataRenderer") } /// Draws all highlight indicators for the values that are currently highlighted. /// /// - parameter indices: the highlighted values @objc open func drawHighlighted(context: CGContext, indices: [Highlight]) { fatalError("drawHighlighted() cannot be called on DataRenderer") } /// An opportunity for initializing internal buffers used for rendering with a new size. /// Since this might do memory allocations, it should only be called if necessary. @objc open func initBuffers() { } @objc open func isDrawingValuesAllowed(dataProvider: ChartDataProvider?) -> Bool { guard let data = dataProvider?.data else { return false } return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * viewPortHandler.scaleX) } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/CombinedChartRenderer.swift ================================================ // // CombinedChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class CombinedChartRenderer: DataRenderer { @objc open weak var chart: CombinedChartView? /// if set to true, all values are drawn above their bars, instead of below their top @objc open var drawValueAboveBarEnabled = true /// if set to true, a grey area is drawn behind each bar that indicates the maximum value @objc open var drawBarShadowEnabled = false internal var _renderers = [DataRenderer]() internal var _drawOrder: [CombinedChartView.DrawOrder] = [.bar, .bubble, .line, .candle, .scatter] @objc public init(chart: CombinedChartView, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.chart = chart createRenderers() } /// Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into consideration. internal func createRenderers() { _renderers = [DataRenderer]() guard let chart = chart else { return } for order in drawOrder { switch (order) { case .bar: if chart.barData !== nil { _renderers.append(BarChartRenderer(dataProvider: chart, animator: animator, viewPortHandler: viewPortHandler)) } break case .line: if chart.lineData !== nil { _renderers.append(LineChartRenderer(dataProvider: chart, animator: animator, viewPortHandler: viewPortHandler)) } break case .candle: if chart.candleData !== nil { _renderers.append(CandleStickChartRenderer(dataProvider: chart, animator: animator, viewPortHandler: viewPortHandler)) } break case .scatter: if chart.scatterData !== nil { _renderers.append(ScatterChartRenderer(dataProvider: chart, animator: animator, viewPortHandler: viewPortHandler)) } break case .bubble: if chart.bubbleData !== nil { _renderers.append(BubbleChartRenderer(dataProvider: chart, animator: animator, viewPortHandler: viewPortHandler)) } break } } } open override func initBuffers() { for renderer in _renderers { renderer.initBuffers() } } open override func drawData(context: CGContext) { for renderer in _renderers { renderer.drawData(context: context) } } open override func drawValues(context: CGContext) { for renderer in _renderers { renderer.drawValues(context: context) } } open override func drawExtras(context: CGContext) { for renderer in _renderers { renderer.drawExtras(context: context) } } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { for renderer in _renderers { var data: ChartData? if renderer is BarChartRenderer { data = (renderer as! BarChartRenderer).dataProvider?.barData } else if renderer is LineChartRenderer { data = (renderer as! LineChartRenderer).dataProvider?.lineData } else if renderer is CandleStickChartRenderer { data = (renderer as! CandleStickChartRenderer).dataProvider?.candleData } else if renderer is ScatterChartRenderer { data = (renderer as! ScatterChartRenderer).dataProvider?.scatterData } else if renderer is BubbleChartRenderer { data = (renderer as! BubbleChartRenderer).dataProvider?.bubbleData } let dataIndex = data == nil ? nil : (chart?.data as? CombinedChartData)?.allData.index(of: data!) let dataIndices = indices.filter{ $0.dataIndex == dataIndex || $0.dataIndex == -1 } renderer.drawHighlighted(context: context, indices: dataIndices) } } /// - returns: The sub-renderer object at the specified index. @objc open func getSubRenderer(index: Int) -> DataRenderer? { if index >= _renderers.count || index < 0 { return nil } else { return _renderers[index] } } /// - returns: All sub-renderers. @objc open var subRenderers: [DataRenderer] { get { return _renderers } set { _renderers = newValue } } // MARK: Accessors /// - returns: `true` if drawing values above bars is enabled, `false` ifnot @objc open var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled } /// - returns: `true` if drawing shadows (maxvalue) for each bar is enabled, `false` ifnot @objc open var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled } /// the order in which the provided data objects should be drawn. /// The earlier you place them in the provided array, the further they will be in the background. /// e.g. if you provide [DrawOrder.Bar, DrawOrder.Line], the bars will be drawn behind the lines. open var drawOrder: [CombinedChartView.DrawOrder] { get { return _drawOrder } set { if newValue.count > 0 { _drawOrder = newValue } } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/HorizontalBarChartRenderer.swift ================================================ // // HorizontalBarChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class HorizontalBarChartRenderer: BarChartRenderer { private class Buffer { var rects = [CGRect]() } public override init(dataProvider: BarChartDataProvider, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(dataProvider: dataProvider, animator: animator, viewPortHandler: viewPortHandler) } // [CGRect] per dataset private var _buffers = [Buffer]() open override func initBuffers() { if let barData = dataProvider?.barData { // Matche buffers count to dataset count if _buffers.count != barData.dataSetCount { while _buffers.count < barData.dataSetCount { _buffers.append(Buffer()) } while _buffers.count > barData.dataSetCount { _buffers.removeLast() } } for i in stride(from: 0, to: barData.dataSetCount, by: 1) { let set = barData.dataSets[i] as! IBarChartDataSet let size = set.entryCount * (set.isStacked ? set.stackSize : 1) if _buffers[i].rects.count != size { _buffers[i].rects = [CGRect](repeating: CGRect(), count: size) } } } else { _buffers.removeAll() } } private func prepareBuffer(dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } let barWidthHalf = barData.barWidth / 2.0 let buffer = _buffers[index] var bufferIndex = 0 let containsStacks = dataSet.isStacked let isInverted = dataProvider.isInverted(axis: dataSet.axisDependency) let phaseY = animator.phaseY var barRect = CGRect() var x: Double var y: Double for i in stride(from: 0, to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } let vals = e.yValues x = e.x y = e.y if !containsStacks || vals == nil { let bottom = CGFloat(x - barWidthHalf) let top = CGFloat(x + barWidthHalf) var right = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) var left = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) // multiply the height of the rect with the phase if right > 0 { right *= CGFloat(phaseY) } else { left *= CGFloat(phaseY) } barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top buffer.rects[bufferIndex] = barRect bufferIndex += 1 } else { var posY = 0.0 var negY = -e.negativeSum var yStart = 0.0 // fill the stack for k in 0 ..< vals!.count { let value = vals![k] if value == 0.0 && (posY == 0.0 || negY == 0.0) { // Take care of the situation of a 0.0 value, which overlaps a non-zero bar y = value yStart = y } else if value >= 0.0 { y = posY yStart = posY + value posY = yStart } else { y = negY yStart = negY + abs(value) negY += abs(value) } let bottom = CGFloat(x - barWidthHalf) let top = CGFloat(x + barWidthHalf) var right = isInverted ? (y <= yStart ? CGFloat(y) : CGFloat(yStart)) : (y >= yStart ? CGFloat(y) : CGFloat(yStart)) var left = isInverted ? (y >= yStart ? CGFloat(y) : CGFloat(yStart)) : (y <= yStart ? CGFloat(y) : CGFloat(yStart)) // multiply the height of the rect with the phase right *= CGFloat(phaseY) left *= CGFloat(phaseY) barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top buffer.rects[bufferIndex] = barRect bufferIndex += 1 } } } } private var _barShadowRectBuffer: CGRect = CGRect() open override func drawDataSet(context: CGContext, dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) prepareBuffer(dataSet: dataSet, index: index) trans.rectValuesToPixel(&_buffers[index].rects) let borderWidth = dataSet.barBorderWidth let borderColor = dataSet.barBorderColor let drawBorder = borderWidth > 0.0 context.saveGState() // draw the bar shadow before the values if dataProvider.isDrawBarShadowEnabled { guard let barData = dataProvider.barData else { return } let barWidth = barData.barWidth let barWidthHalf = barWidth / 2.0 var x: Double = 0.0 for i in stride(from: 0, to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } x = e.x _barShadowRectBuffer.origin.y = CGFloat(x - barWidthHalf) _barShadowRectBuffer.size.height = CGFloat(barWidth) trans.rectValueToPixel(&_barShadowRectBuffer) if !viewPortHandler.isInBoundsTop(_barShadowRectBuffer.origin.y + _barShadowRectBuffer.size.height) { break } if !viewPortHandler.isInBoundsBottom(_barShadowRectBuffer.origin.y) { continue } _barShadowRectBuffer.origin.x = viewPortHandler.contentLeft _barShadowRectBuffer.size.width = viewPortHandler.contentWidth context.setFillColor(dataSet.barShadowColor.cgColor) context.fill(_barShadowRectBuffer) } } let buffer = _buffers[index] let isSingleColor = dataSet.colors.count == 1 if isSingleColor { context.setFillColor(dataSet.color(atIndex: 0).cgColor) } for j in stride(from: 0, to: buffer.rects.count, by: 1) { let barRect = buffer.rects[j] if (!viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) { break } if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) { continue } if !isSingleColor { // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. context.setFillColor(dataSet.color(atIndex: j).cgColor) } context.fill(barRect) if drawBorder { context.setStrokeColor(borderColor.cgColor) context.setLineWidth(borderWidth) context.stroke(barRect) } } context.restoreGState() } open override func prepareBarHighlight( x: Double, y1: Double, y2: Double, barWidthHalf: Double, trans: Transformer, rect: inout CGRect) { let top = x - barWidthHalf let bottom = x + barWidthHalf let left = y1 let right = y2 rect.origin.x = CGFloat(left) rect.origin.y = CGFloat(top) rect.size.width = CGFloat(right - left) rect.size.height = CGFloat(bottom - top) trans.rectValueToPixelHorizontal(&rect, phaseY: animator.phaseY) } open override func drawValues(context: CGContext) { // if values are drawn if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataProvider = dataProvider, let barData = dataProvider.barData else { return } var dataSets = barData.dataSets let textAlign = NSTextAlignment.left let valueOffsetPlus: CGFloat = 5.0 var posOffset: CGFloat var negOffset: CGFloat let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled for dataSetIndex in 0 ..< barData.dataSetCount { guard let dataSet = dataSets[dataSetIndex] as? IBarChartDataSet else { continue } if !shouldDrawValues(forDataSet: dataSet) || !(dataSet.isDrawIconsEnabled && dataSet.isVisible) { continue } let isInverted = dataProvider.isInverted(axis: dataSet.axisDependency) let valueFont = dataSet.valueFont let yOffset = -valueFont.lineHeight / 2.0 guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY let iconsOffset = dataSet.iconsOffset let buffer = _buffers[dataSetIndex] // if only single values are drawn (sum) if !dataSet.isStacked { for j in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } let rect = buffer.rects[j] let y = rect.origin.y + rect.size.height / 2.0 if !viewPortHandler.isInBoundsTop(rect.origin.y) { break } if !viewPortHandler.isInBoundsX(rect.origin.x) { continue } if !viewPortHandler.isInBoundsBottom(rect.origin.y) { continue } let val = e.y let valueText = formatter.stringForValue( val, entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler) // calculate the correct offset depending on the draw position of the value let valueTextWidth = valueText.size(withAttributes: [NSAttributedString.Key.font: valueFont]).width posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)) negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) if isInverted { posOffset = -posOffset - valueTextWidth negOffset = -negOffset - valueTextWidth } if dataSet.isDrawValuesEnabled { drawValue( context: context, value: valueText, xPos: (rect.origin.x + rect.size.width) + (val >= 0.0 ? posOffset : negOffset), yPos: y + yOffset, font: valueFont, align: textAlign, color: dataSet.valueTextColorAt(j)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { var px = (rect.origin.x + rect.size.width) + (val >= 0.0 ? posOffset : negOffset) var py = y px += iconsOffset.x py += iconsOffset.y ChartUtils.drawImage( context: context, image: icon, x: px, y: py, size: icon.size) } } } else { // if each value of a potential stack should be drawn var bufferIndex = 0 for index in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(index) as? BarChartDataEntry else { continue } let rect = buffer.rects[bufferIndex] let vals = e.yValues // we still draw stacked bars, but there is one non-stacked in between if vals == nil { if !viewPortHandler.isInBoundsTop(rect.origin.y) { break } if !viewPortHandler.isInBoundsX(rect.origin.x) { continue } if !viewPortHandler.isInBoundsBottom(rect.origin.y) { continue } let val = e.y let valueText = formatter.stringForValue( val, entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler) // calculate the correct offset depending on the draw position of the value let valueTextWidth = valueText.size(withAttributes: [NSAttributedString.Key.font: valueFont]).width posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)) negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) if isInverted { posOffset = -posOffset - valueTextWidth negOffset = -negOffset - valueTextWidth } if dataSet.isDrawValuesEnabled { drawValue( context: context, value: valueText, xPos: (rect.origin.x + rect.size.width) + (val >= 0.0 ? posOffset : negOffset), yPos: rect.origin.y + yOffset, font: valueFont, align: textAlign, color: dataSet.valueTextColorAt(index)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { var px = (rect.origin.x + rect.size.width) + (val >= 0.0 ? posOffset : negOffset) var py = rect.origin.y px += iconsOffset.x py += iconsOffset.y ChartUtils.drawImage( context: context, image: icon, x: px, y: py, size: icon.size) } } else { let vals = vals! var transformed = [CGPoint]() var posY = 0.0 var negY = -e.negativeSum for k in 0 ..< vals.count { let value = vals[k] var y: Double if value == 0.0 && (posY == 0.0 || negY == 0.0) { // Take care of the situation of a 0.0 value, which overlaps a non-zero bar y = value } else if value >= 0.0 { posY += value y = posY } else { y = negY negY -= value } transformed.append(CGPoint(x: CGFloat(y * phaseY), y: 0.0)) } trans.pointValuesToPixel(&transformed) for k in 0 ..< transformed.count { let val = vals[k] let valueText = formatter.stringForValue( val, entry: e, dataSetIndex: dataSetIndex, viewPortHandler: viewPortHandler) // calculate the correct offset depending on the draw position of the value let valueTextWidth = valueText.size(withAttributes: [NSAttributedString.Key.font: valueFont]).width posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)) negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) if isInverted { posOffset = -posOffset - valueTextWidth negOffset = -negOffset - valueTextWidth } let drawBelow = (val == 0.0 && negY == 0.0 && posY > 0.0) || val < 0.0 let x = transformed[k].x + (drawBelow ? negOffset : posOffset) let y = rect.origin.y + rect.size.height / 2.0 if (!viewPortHandler.isInBoundsTop(y)) { break } if (!viewPortHandler.isInBoundsX(x)) { continue } if (!viewPortHandler.isInBoundsBottom(y)) { continue } if dataSet.isDrawValuesEnabled { drawValue(context: context, value: valueText, xPos: x, yPos: y + yOffset, font: valueFont, align: textAlign, color: dataSet.valueTextColorAt(index)) } if let icon = e.icon, dataSet.isDrawIconsEnabled { ChartUtils.drawImage( context: context, image: icon, x: x + iconsOffset.x, y: y + iconsOffset.y, size: icon.size) } } } bufferIndex = vals == nil ? (bufferIndex + 1) : (bufferIndex + vals!.count) } } } } } open override func isDrawingValuesAllowed(dataProvider: ChartDataProvider?) -> Bool { guard let data = dataProvider?.data else { return false } return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * self.viewPortHandler.scaleY) } /// Sets the drawing position of the highlight object based on the riven bar-rect. internal override func setHighlightDrawPos(highlight high: Highlight, barRect: CGRect) { high.setDraw(x: barRect.midY, y: barRect.origin.x + barRect.size.width) } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/LegendRenderer.swift ================================================ // // LegendRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartLegendRenderer) open class LegendRenderer: Renderer { /// the legend object this renderer renders @objc open var legend: Legend? @objc public init(viewPortHandler: ViewPortHandler, legend: Legend?) { super.init(viewPortHandler: viewPortHandler) self.legend = legend } /// Prepares the legend and calculates all needed forms, labels and colors. @objc open func computeLegend(data: ChartData) { guard let legend = legend else { return } if !legend.isLegendCustom { var entries: [LegendEntry] = [] // loop for building up the colors and labels used in the legend for i in 0.. 0 { context.setLineDash(phase: formLineDashPhase, lengths: formLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.setStrokeColor(formColor.cgColor) _formLineSegmentsBuffer[0].x = x _formLineSegmentsBuffer[0].y = y _formLineSegmentsBuffer[1].x = x + formSize _formLineSegmentsBuffer[1].y = y context.strokeLineSegments(between: _formLineSegmentsBuffer) } } /// Draws the provided label at the given position. @objc open func drawLabel(context: CGContext, x: CGFloat, y: CGFloat, label: String, font: NSUIFont, textColor: NSUIColor) { ChartUtils.drawText(context: context, text: label, point: CGPoint(x: x, y: y), align: .left, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor]) } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/LineChartRenderer.swift ================================================ // // LineChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class LineChartRenderer: LineRadarRenderer { @objc open weak var dataProvider: LineChartDataProvider? @objc public init(dataProvider: LineChartDataProvider, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.dataProvider = dataProvider } open override func drawData(context: CGContext) { guard let lineData = dataProvider?.lineData else { return } for i in 0 ..< lineData.dataSetCount { guard let set = lineData.getDataSetByIndex(i) else { continue } if set.isVisible { if !(set is ILineChartDataSet) { fatalError("Datasets for LineChartRenderer must conform to ILineChartDataSet") } drawDataSet(context: context, dataSet: set as! ILineChartDataSet) } } } @objc open func drawDataSet(context: CGContext, dataSet: ILineChartDataSet) { if dataSet.entryCount < 1 { return } context.saveGState() context.setLineWidth(dataSet.lineWidth) if dataSet.lineDashLengths != nil { context.setLineDash(phase: dataSet.lineDashPhase, lengths: dataSet.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } // if drawing cubic lines is enabled switch dataSet.mode { case .linear: fallthrough case .stepped: drawLinear(context: context, dataSet: dataSet) case .cubicBezier: drawCubicBezier(context: context, dataSet: dataSet) case .horizontalBezier: drawHorizontalBezier(context: context, dataSet: dataSet) } context.restoreGState() } @objc open func drawCubicBezier(context: CGContext, dataSet: ILineChartDataSet) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) // get the color that is specified for this position from the DataSet let drawingColor = dataSet.colors.first! let intensity = dataSet.cubicIntensity // the path for the cubic-spline let cubicPath = CGMutablePath() let valueToPixelMatrix = trans.valueToPixelMatrix if _xBounds.range >= 1 { var prevDx: CGFloat = 0.0 var prevDy: CGFloat = 0.0 var curDx: CGFloat = 0.0 var curDy: CGFloat = 0.0 // Take an extra point from the left, and an extra from the right. // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart. // So in the starting `prev` and `cur`, go -2, -1 // And in the `lastIndex`, add +1 let firstIndex = _xBounds.min + 1 let lastIndex = _xBounds.min + _xBounds.range var prevPrev: ChartDataEntry! = nil var prev: ChartDataEntry! = dataSet.entryForIndex(max(firstIndex - 2, 0)) var cur: ChartDataEntry! = dataSet.entryForIndex(max(firstIndex - 1, 0)) var next: ChartDataEntry! = cur var nextIndex: Int = -1 if cur == nil { return } // let the spline start cubicPath.move(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix) for j in stride(from: firstIndex, through: lastIndex, by: 1) { prevPrev = prev prev = cur cur = nextIndex == j ? next : dataSet.entryForIndex(j) nextIndex = j + 1 < dataSet.entryCount ? j + 1 : j next = dataSet.entryForIndex(nextIndex) if next == nil { break } prevDx = CGFloat(cur.x - prevPrev.x) * intensity prevDy = CGFloat(cur.y - prevPrev.y) * intensity curDx = CGFloat(next.x - prev.x) * intensity curDy = CGFloat(next.y - prev.y) * intensity cubicPath.addCurve( to: CGPoint( x: CGFloat(cur.x), y: CGFloat(cur.y) * CGFloat(phaseY)), control1: CGPoint( x: CGFloat(prev.x) + prevDx, y: (CGFloat(prev.y) + prevDy) * CGFloat(phaseY)), control2: CGPoint( x: CGFloat(cur.x) - curDx, y: (CGFloat(cur.y) - curDy) * CGFloat(phaseY)), transform: valueToPixelMatrix) } } context.saveGState() if dataSet.isDrawFilledEnabled { // Copy this path because we make changes to it let fillPath = cubicPath.mutableCopy() drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: _xBounds) } context.beginPath() context.addPath(cubicPath) context.setStrokeColor(drawingColor.cgColor) context.strokePath() context.restoreGState() } @objc open func drawHorizontalBezier(context: CGContext, dataSet: ILineChartDataSet) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) // get the color that is specified for this position from the DataSet let drawingColor = dataSet.colors.first! // the path for the cubic-spline let cubicPath = CGMutablePath() let valueToPixelMatrix = trans.valueToPixelMatrix if _xBounds.range >= 1 { var prev: ChartDataEntry! = dataSet.entryForIndex(_xBounds.min) var cur: ChartDataEntry! = prev if cur == nil { return } // let the spline start cubicPath.move(to: CGPoint(x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix) for j in stride(from: (_xBounds.min + 1), through: _xBounds.range + _xBounds.min, by: 1) { prev = cur cur = dataSet.entryForIndex(j) let cpx = CGFloat(prev.x + (cur.x - prev.x) / 2.0) cubicPath.addCurve( to: CGPoint( x: CGFloat(cur.x), y: CGFloat(cur.y * phaseY)), control1: CGPoint( x: cpx, y: CGFloat(prev.y * phaseY)), control2: CGPoint( x: cpx, y: CGFloat(cur.y * phaseY)), transform: valueToPixelMatrix) } } context.saveGState() if dataSet.isDrawFilledEnabled { // Copy this path because we make changes to it let fillPath = cubicPath.mutableCopy() drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: _xBounds) } context.beginPath() context.addPath(cubicPath) context.setStrokeColor(drawingColor.cgColor) context.strokePath() context.restoreGState() } open func drawCubicFill( context: CGContext, dataSet: ILineChartDataSet, spline: CGMutablePath, matrix: CGAffineTransform, bounds: XBounds) { guard let dataProvider = dataProvider else { return } if bounds.range <= 0 { return } let fillMin = dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0 var pt1 = CGPoint(x: CGFloat(dataSet.entryForIndex(bounds.min + bounds.range)?.x ?? 0.0), y: fillMin) var pt2 = CGPoint(x: CGFloat(dataSet.entryForIndex(bounds.min)?.x ?? 0.0), y: fillMin) pt1 = pt1.applying(matrix) pt2 = pt2.applying(matrix) spline.addLine(to: pt1) spline.addLine(to: pt2) spline.closeSubpath() if dataSet.fill != nil { drawFilledPath(context: context, path: spline, fill: dataSet.fill!, fillAlpha: dataSet.fillAlpha) } else { drawFilledPath(context: context, path: spline, fillColor: dataSet.fillColor, fillAlpha: dataSet.fillAlpha) } } private var _lineSegments = [CGPoint](repeating: CGPoint(), count: 2) @objc open func drawLinear(context: CGContext, dataSet: ILineChartDataSet) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix let entryCount = dataSet.entryCount let isDrawSteppedEnabled = dataSet.mode == .stepped let pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2 let phaseY = animator.phaseY _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) // if drawing filled is enabled if dataSet.isDrawFilledEnabled && entryCount > 0 { drawLinearFill(context: context, dataSet: dataSet, trans: trans, bounds: _xBounds) } context.saveGState() context.setLineCap(dataSet.lineCapType) // more than 1 color if dataSet.colors.count > 1 { if _lineSegments.count != pointsPerEntryPair { // Allocate once in correct size _lineSegments = [CGPoint](repeating: CGPoint(), count: pointsPerEntryPair) } for j in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { var e: ChartDataEntry! = dataSet.entryForIndex(j) if e == nil { continue } _lineSegments[0].x = CGFloat(e.x) _lineSegments[0].y = CGFloat(e.y * phaseY) if j < _xBounds.max { e = dataSet.entryForIndex(j + 1) if e == nil { break } if isDrawSteppedEnabled { _lineSegments[1] = CGPoint(x: CGFloat(e.x), y: _lineSegments[0].y) _lineSegments[2] = _lineSegments[1] _lineSegments[3] = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)) } else { _lineSegments[1] = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)) } } else { _lineSegments[1] = _lineSegments[0] } for i in 0..<_lineSegments.count { _lineSegments[i] = _lineSegments[i].applying(valueToPixelMatrix) } if (!viewPortHandler.isInBoundsRight(_lineSegments[0].x)) { break } // make sure the lines don't do shitty things outside bounds if !viewPortHandler.isInBoundsLeft(_lineSegments[1].x) || (!viewPortHandler.isInBoundsTop(_lineSegments[0].y) && !viewPortHandler.isInBoundsBottom(_lineSegments[1].y)) { continue } // get the color that is set for this line-segment context.setStrokeColor(dataSet.color(atIndex: j).cgColor) context.strokeLineSegments(between: _lineSegments) } } else { // only one color per dataset var e1: ChartDataEntry! var e2: ChartDataEntry! e1 = dataSet.entryForIndex(_xBounds.min) if e1 != nil { context.beginPath() var firstPoint = true for x in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { e1 = dataSet.entryForIndex(x == 0 ? 0 : (x - 1)) e2 = dataSet.entryForIndex(x) if e1 == nil || e2 == nil { continue } let pt = CGPoint( x: CGFloat(e1.x), y: CGFloat(e1.y * phaseY) ).applying(valueToPixelMatrix) if firstPoint { context.move(to: pt) firstPoint = false } else { context.addLine(to: pt) } if isDrawSteppedEnabled { context.addLine(to: CGPoint( x: CGFloat(e2.x), y: CGFloat(e1.y * phaseY) ).applying(valueToPixelMatrix)) } context.addLine(to: CGPoint( x: CGFloat(e2.x), y: CGFloat(e2.y * phaseY) ).applying(valueToPixelMatrix)) } if !firstPoint { context.setStrokeColor(dataSet.color(atIndex: 0).cgColor) context.strokePath() } } } context.restoreGState() } open func drawLinearFill(context: CGContext, dataSet: ILineChartDataSet, trans: Transformer, bounds: XBounds) { guard let dataProvider = dataProvider else { return } let filled = generateFilledPath( dataSet: dataSet, fillMin: dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0, bounds: bounds, matrix: trans.valueToPixelMatrix) if dataSet.fill != nil { drawFilledPath(context: context, path: filled, fill: dataSet.fill!, fillAlpha: dataSet.fillAlpha) } else { drawFilledPath(context: context, path: filled, fillColor: dataSet.fillColor, fillAlpha: dataSet.fillAlpha) } } /// Generates the path that is used for filled drawing. private func generateFilledPath(dataSet: ILineChartDataSet, fillMin: CGFloat, bounds: XBounds, matrix: CGAffineTransform) -> CGPath { let phaseY = animator.phaseY let isDrawSteppedEnabled = dataSet.mode == .stepped let matrix = matrix var e: ChartDataEntry! let filled = CGMutablePath() e = dataSet.entryForIndex(bounds.min) if e != nil { filled.move(to: CGPoint(x: CGFloat(e.x), y: fillMin), transform: matrix) filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)), transform: matrix) } // create a new path for x in stride(from: (bounds.min + 1), through: bounds.range + bounds.min, by: 1) { guard let e = dataSet.entryForIndex(x) else { continue } if isDrawSteppedEnabled { guard let ePrev = dataSet.entryForIndex(x-1) else { continue } filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(ePrev.y * phaseY)), transform: matrix) } filled.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)), transform: matrix) } // close up e = dataSet.entryForIndex(bounds.range + bounds.min) if e != nil { filled.addLine(to: CGPoint(x: CGFloat(e.x), y: fillMin), transform: matrix) } filled.closeSubpath() return filled } open override func drawValues(context: CGContext) { guard let dataProvider = dataProvider, let lineData = dataProvider.lineData else { return } if isDrawingValuesAllowed(dataProvider: dataProvider) { var dataSets = lineData.dataSets let phaseY = animator.phaseY var pt = CGPoint() for i in 0 ..< dataSets.count { guard let dataSet = dataSets[i] as? ILineChartDataSet else { continue } if !shouldDrawValues(forDataSet: dataSet) { continue } let valueFont = dataSet.valueFont guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix let iconsOffset = dataSet.iconsOffset // make sure the values do not interfear with the circles var valOffset = Int(dataSet.circleRadius * 1.75) if !dataSet.isDrawCirclesEnabled { valOffset = valOffset / 2 } _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) for j in stride(from: _xBounds.min, through: min(_xBounds.min + _xBounds.range, _xBounds.max), by: 1) { guard let e = dataSet.entryForIndex(j) else { break } pt.x = CGFloat(e.x) pt.y = CGFloat(e.y * phaseY) pt = pt.applying(valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) { break } if (!viewPortHandler.isInBoundsLeft(pt.x) || !viewPortHandler.isInBoundsY(pt.y)) { continue } if dataSet.isDrawValuesEnabled { ChartUtils.drawText( context: context, text: formatter.stringForValue( e.y, entry: e, dataSetIndex: i, viewPortHandler: viewPortHandler), point: CGPoint( x: pt.x, y: pt.y - CGFloat(valOffset) - valueFont.lineHeight), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: dataSet.valueTextColorAt(j)]) } if let icon = e.icon, dataSet.isDrawIconsEnabled { ChartUtils.drawImage(context: context, image: icon, x: pt.x + iconsOffset.x, y: pt.y + iconsOffset.y, size: icon.size) } } } } } open override func drawExtras(context: CGContext) { drawCircles(context: context) } private func drawCircles(context: CGContext) { guard let dataProvider = dataProvider, let lineData = dataProvider.lineData else { return } let phaseY = animator.phaseY let dataSets = lineData.dataSets var pt = CGPoint() var rect = CGRect() context.saveGState() for i in 0 ..< dataSets.count { guard let dataSet = lineData.getDataSetByIndex(i) as? ILineChartDataSet else { continue } if !dataSet.isVisible || !dataSet.isDrawCirclesEnabled || dataSet.entryCount == 0 { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) let circleRadius = dataSet.circleRadius let circleDiameter = circleRadius * 2.0 let circleHoleRadius = dataSet.circleHoleRadius let circleHoleDiameter = circleHoleRadius * 2.0 let drawCircleHole = dataSet.isDrawCircleHoleEnabled && circleHoleRadius < circleRadius && circleHoleRadius > 0.0 let drawTransparentCircleHole = drawCircleHole && (dataSet.circleHoleColor == nil || dataSet.circleHoleColor == NSUIColor.clear) for j in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { guard let e = dataSet.entryForIndex(j) else { break } pt.x = CGFloat(e.x) pt.y = CGFloat(e.y * phaseY) pt = pt.applying(valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) { break } // make sure the circles don't do shitty things outside bounds if (!viewPortHandler.isInBoundsLeft(pt.x) || !viewPortHandler.isInBoundsY(pt.y)) { continue } context.setFillColor(dataSet.getCircleColor(atIndex: j)!.cgColor) rect.origin.x = pt.x - circleRadius rect.origin.y = pt.y - circleRadius rect.size.width = circleDiameter rect.size.height = circleDiameter if drawTransparentCircleHole { // Begin path for circle with hole context.beginPath() context.addEllipse(in: rect) // Cut hole in path rect.origin.x = pt.x - circleHoleRadius rect.origin.y = pt.y - circleHoleRadius rect.size.width = circleHoleDiameter rect.size.height = circleHoleDiameter context.addEllipse(in: rect) // Fill in-between context.fillPath(using: .evenOdd) } else { context.fillEllipse(in: rect) if drawCircleHole { context.setFillColor(dataSet.circleHoleColor!.cgColor) // The hole rect rect.origin.x = pt.x - circleHoleRadius rect.origin.y = pt.y - circleHoleRadius rect.size.width = circleHoleDiameter rect.size.height = circleHoleDiameter context.fillEllipse(in: rect) } } } } context.restoreGState() } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let dataProvider = dataProvider, let lineData = dataProvider.lineData else { return } let chartXMax = dataProvider.chartXMax context.saveGState() for high in indices { guard let set = lineData.getDataSetByIndex(high.dataSetIndex) as? ILineChartDataSet , set.isHighlightEnabled else { continue } guard let e = set.entryForXValue(high.x, closestToY: high.y) else { continue } if !isInBoundsX(entry: e, dataSet: set) { continue } context.setStrokeColor(set.highlightColor.cgColor) context.setLineWidth(set.highlightLineWidth) if set.highlightLineDashLengths != nil { context.setLineDash(phase: set.highlightLineDashPhase, lengths: set.highlightLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } let x = high.x // get the x-position let y = high.y * Double(animator.phaseY) if x > chartXMax * animator.phaseX { continue } let trans = dataProvider.getTransformer(forAxis: set.axisDependency) let pt = trans.pixelForValues(x: x, y: y) high.setDraw(pt: pt) // draw the lines drawHighlightLines(context: context, point: pt, set: set) } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/LineRadarRenderer.swift ================================================ // // LineRadarRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(LineRadarChartRenderer) open class LineRadarRenderer: LineScatterCandleRadarRenderer { public override init(animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) } /// Draws the provided path in filled mode with the provided drawable. @objc open func drawFilledPath(context: CGContext, path: CGPath, fill: Fill, fillAlpha: CGFloat) { context.saveGState() context.beginPath() context.addPath(path) // filled is usually drawn with less alpha context.setAlpha(fillAlpha) fill.fillPath(context: context, rect: viewPortHandler.contentRect) context.restoreGState() } /// Draws the provided path in filled mode with the provided color and alpha. @objc open func drawFilledPath(context: CGContext, path: CGPath, fillColor: NSUIColor, fillAlpha: CGFloat) { context.saveGState() context.beginPath() context.addPath(path) // filled is usually drawn with less alpha context.setAlpha(fillAlpha) context.setFillColor(fillColor.cgColor) context.fillPath() context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/LineScatterCandleRadarRenderer.swift ================================================ // // LineScatterCandleRadarRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(LineScatterCandleRadarChartRenderer) open class LineScatterCandleRadarRenderer: BarLineScatterCandleBubbleRenderer { public override init(animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) } /// Draws vertical & horizontal highlight-lines if enabled. /// :param: context /// :param: points /// :param: horizontal /// :param: vertical @objc open func drawHighlightLines(context: CGContext, point: CGPoint, set: ILineScatterCandleRadarChartDataSet) { // draw vertical highlight lines if set.isVerticalHighlightIndicatorEnabled { context.beginPath() context.move(to: CGPoint(x: point.x, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: point.x, y: viewPortHandler.contentBottom)) context.strokePath() } // draw horizontal highlight lines if set.isHorizontalHighlightIndicatorEnabled { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: point.y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: point.y)) context.strokePath() } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/PieChartRenderer.swift ================================================ // // PieChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class PieChartRenderer: DataRenderer { @objc open weak var chart: PieChartView? @objc public init(chart: PieChartView, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.chart = chart } open override func drawData(context: CGContext) { guard let chart = chart else { return } let pieData = chart.data if pieData != nil { for set in pieData!.dataSets as! [IPieChartDataSet] { if set.isVisible && set.entryCount > 0 { drawDataSet(context: context, dataSet: set) } } } } @objc open func calculateMinimumRadiusForSpacedSlice( center: CGPoint, radius: CGFloat, angle: CGFloat, arcStartPointX: CGFloat, arcStartPointY: CGFloat, startAngle: CGFloat, sweepAngle: CGFloat) -> CGFloat { let angleMiddle = startAngle + sweepAngle / 2.0 // Other point of the arc let arcEndPointX = center.x + radius * cos((startAngle + sweepAngle).DEG2RAD) let arcEndPointY = center.y + radius * sin((startAngle + sweepAngle).DEG2RAD) // Middle point on the arc let arcMidPointX = center.x + radius * cos(angleMiddle.DEG2RAD) let arcMidPointY = center.y + radius * sin(angleMiddle.DEG2RAD) // This is the base of the contained triangle let basePointsDistance = sqrt( pow(arcEndPointX - arcStartPointX, 2) + pow(arcEndPointY - arcStartPointY, 2)) // After reducing space from both sides of the "slice", // the angle of the contained triangle should stay the same. // So let's find out the height of that triangle. let containedTriangleHeight = (basePointsDistance / 2.0 * tan((180.0 - angle).DEG2RAD / 2.0)) // Now we subtract that from the radius var spacedRadius = radius - containedTriangleHeight // And now subtract the height of the arc that's between the triangle and the outer circle spacedRadius -= sqrt( pow(arcMidPointX - (arcEndPointX + arcStartPointX) / 2.0, 2) + pow(arcMidPointY - (arcEndPointY + arcStartPointY) / 2.0, 2)) return spacedRadius } /// Calculates the sliceSpace to use based on visible values and their size compared to the set sliceSpace. @objc open func getSliceSpace(dataSet: IPieChartDataSet) -> CGFloat { guard dataSet.automaticallyDisableSliceSpacing, let data = chart?.data as? PieChartData else { return dataSet.sliceSpace } let spaceSizeRatio = dataSet.sliceSpace / min(viewPortHandler.contentWidth, viewPortHandler.contentHeight) let minValueRatio = dataSet.yMin / data.yValueSum * 2.0 let sliceSpace = spaceSizeRatio > CGFloat(minValueRatio) ? 0.0 : dataSet.sliceSpace return sliceSpace } @objc open func drawDataSet(context: CGContext, dataSet: IPieChartDataSet) { guard let chart = chart else {return } var angle: CGFloat = 0.0 let rotationAngle = chart.rotationAngle let phaseX = animator.phaseX let phaseY = animator.phaseY let entryCount = dataSet.entryCount var drawAngles = chart.drawAngles let center = chart.centerCircleBox let radius = chart.radius let drawInnerArc = chart.drawHoleEnabled && !chart.drawSlicesUnderHoleEnabled let userInnerRadius = drawInnerArc ? radius * chart.holeRadiusPercent : 0.0 var visibleAngleCount = 0 for j in 0 ..< entryCount { guard let e = dataSet.entryForIndex(j) else { continue } if ((abs(e.y) > Double.ulpOfOne)) { visibleAngleCount += 1 } } let sliceSpace = visibleAngleCount <= 1 ? 0.0 : getSliceSpace(dataSet: dataSet) context.saveGState() for j in 0 ..< entryCount { let sliceAngle = drawAngles[j] var innerRadius = userInnerRadius guard let e = dataSet.entryForIndex(j) else { continue } // draw only if the value is greater than zero if (abs(e.y) > Double.ulpOfOne) { if !chart.needsHighlight(index: j) { let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 context.setFillColor(dataSet.color(atIndex: j).cgColor) let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : sliceSpace / radius.DEG2RAD let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * CGFloat(phaseY) var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * CGFloat(phaseY) if sweepAngleOuter < 0.0 { sweepAngleOuter = 0.0 } let arcStartPointX = center.x + radius * cos(startAngleOuter.DEG2RAD) let arcStartPointY = center.y + radius * sin(startAngleOuter.DEG2RAD) let path = CGMutablePath() path.move(to: CGPoint(x: arcStartPointX, y: arcStartPointY)) path.addRelativeArc(center: center, radius: radius, startAngle: startAngleOuter.DEG2RAD, delta: sweepAngleOuter.DEG2RAD) if drawInnerArc && (innerRadius > 0.0 || accountForSliceSpacing) { if accountForSliceSpacing { var minSpacedRadius = calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, angle: sliceAngle * CGFloat(phaseY), arcStartPointX: arcStartPointX, arcStartPointY: arcStartPointY, startAngle: startAngleOuter, sweepAngle: sweepAngleOuter) if minSpacedRadius < 0.0 { minSpacedRadius = -minSpacedRadius } innerRadius = min(max(innerRadius, minSpacedRadius), radius) } let sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.0 ? 0.0 : sliceSpace / innerRadius.DEG2RAD let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * CGFloat(phaseY) var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * CGFloat(phaseY) if sweepAngleInner < 0.0 { sweepAngleInner = 0.0 } let endAngleInner = startAngleInner + sweepAngleInner path.addLine( to: CGPoint( x: center.x + innerRadius * cos(endAngleInner.DEG2RAD), y: center.y + innerRadius * sin(endAngleInner.DEG2RAD))) path.addRelativeArc(center: center, radius: innerRadius, startAngle: endAngleInner.DEG2RAD, delta: -sweepAngleInner.DEG2RAD) } else { if accountForSliceSpacing { let angleMiddle = startAngleOuter + sweepAngleOuter / 2.0 let sliceSpaceOffset = calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, angle: sliceAngle * CGFloat(phaseY), arcStartPointX: arcStartPointX, arcStartPointY: arcStartPointY, startAngle: startAngleOuter, sweepAngle: sweepAngleOuter) let arcEndPointX = center.x + sliceSpaceOffset * cos(angleMiddle.DEG2RAD) let arcEndPointY = center.y + sliceSpaceOffset * sin(angleMiddle.DEG2RAD) path.addLine( to: CGPoint( x: arcEndPointX, y: arcEndPointY)) } else { path.addLine(to: center) } } path.closeSubpath() context.beginPath() context.addPath(path) context.fillPath(using: .evenOdd) } } angle += sliceAngle * CGFloat(phaseX) } context.restoreGState() } open override func drawValues(context: CGContext) { guard let chart = chart, let data = chart.data else { return } let center = chart.centerCircleBox // get whole the radius let radius = chart.radius let rotationAngle = chart.rotationAngle var drawAngles = chart.drawAngles var absoluteAngles = chart.absoluteAngles let phaseX = animator.phaseX let phaseY = animator.phaseY var labelRadiusOffset = radius / 10.0 * 3.0 if chart.drawHoleEnabled { labelRadiusOffset = (radius - (radius * chart.holeRadiusPercent)) / 2.0 } let labelRadius = radius - labelRadiusOffset let dataSets = data.dataSets let yValueSum = (data as! PieChartData).yValueSum let drawEntryLabels = chart.isDrawEntryLabelsEnabled let usePercentValuesEnabled = chart.usePercentValuesEnabled let entryLabelColor = chart.entryLabelColor let entryLabelFont = chart.entryLabelFont var angle: CGFloat = 0.0 var xIndex = 0 context.saveGState() defer { context.restoreGState() } for i in 0 ..< dataSets.count { guard let dataSet = dataSets[i] as? IPieChartDataSet else { continue } let drawValues = dataSet.isDrawValuesEnabled if !drawValues && !drawEntryLabels && !dataSet.isDrawIconsEnabled { continue } let iconsOffset = dataSet.iconsOffset let xValuePosition = dataSet.xValuePosition let yValuePosition = dataSet.yValuePosition let valueFont = dataSet.valueFont let entryLabelFont = dataSet.entryLabelFont let lineHeight = valueFont.lineHeight guard let formatter = dataSet.valueFormatter else { continue } for j in 0 ..< dataSet.entryCount { guard let e = dataSet.entryForIndex(j) else { continue } let pe = e as? PieChartDataEntry if xIndex == 0 { angle = 0.0 } else { angle = absoluteAngles[xIndex - 1] * CGFloat(phaseX) } let sliceAngle = drawAngles[xIndex] let sliceSpace = getSliceSpace(dataSet: dataSet) let sliceSpaceMiddleAngle = sliceSpace / labelRadius.DEG2RAD // offset needed to center the drawn text in the slice let angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.0) / 2.0 angle = angle + angleOffset let transformedAngle = rotationAngle + angle * CGFloat(phaseY) let value = usePercentValuesEnabled ? e.y / yValueSum * 100.0 : e.y let valueText = formatter.stringForValue( value, entry: e, dataSetIndex: i, viewPortHandler: viewPortHandler) let sliceXBase = cos(transformedAngle.DEG2RAD) let sliceYBase = sin(transformedAngle.DEG2RAD) let drawXOutside = drawEntryLabels && xValuePosition == .outsideSlice let drawYOutside = drawValues && yValuePosition == .outsideSlice let drawXInside = drawEntryLabels && xValuePosition == .insideSlice let drawYInside = drawValues && yValuePosition == .insideSlice let valueTextColor = dataSet.valueTextColorAt(j) let entryLabelColor = dataSet.entryLabelColor if drawXOutside || drawYOutside { let valueLineLength1 = dataSet.valueLinePart1Length let valueLineLength2 = dataSet.valueLinePart2Length let valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage var pt2: CGPoint var labelPoint: CGPoint var align: NSTextAlignment var line1Radius: CGFloat if chart.drawHoleEnabled { line1Radius = (radius - (radius * chart.holeRadiusPercent)) * valueLinePart1OffsetPercentage + (radius * chart.holeRadiusPercent) } else { line1Radius = radius * valueLinePart1OffsetPercentage } let polyline2Length = dataSet.valueLineVariableLength ? labelRadius * valueLineLength2 * abs(sin(transformedAngle.DEG2RAD)) : labelRadius * valueLineLength2 let pt0 = CGPoint( x: line1Radius * sliceXBase + center.x, y: line1Radius * sliceYBase + center.y) let pt1 = CGPoint( x: labelRadius * (1 + valueLineLength1) * sliceXBase + center.x, y: labelRadius * (1 + valueLineLength1) * sliceYBase + center.y) if transformedAngle.truncatingRemainder(dividingBy: 360.0) >= 90.0 && transformedAngle.truncatingRemainder(dividingBy: 360.0) <= 270.0 { pt2 = CGPoint(x: pt1.x - polyline2Length, y: pt1.y) align = .right labelPoint = CGPoint(x: pt2.x - 5, y: pt2.y - lineHeight) } else { pt2 = CGPoint(x: pt1.x + polyline2Length, y: pt1.y) align = .left labelPoint = CGPoint(x: pt2.x + 5, y: pt2.y - lineHeight) } if dataSet.valueLineColor != nil { context.setStrokeColor(dataSet.valueLineColor!.cgColor) context.setLineWidth(dataSet.valueLineWidth) context.move(to: CGPoint(x: pt0.x, y: pt0.y)) context.addLine(to: CGPoint(x: pt1.x, y: pt1.y)) context.addLine(to: CGPoint(x: pt2.x, y: pt2.y)) context.drawPath(using: CGPathDrawingMode.stroke) } if drawXOutside && drawYOutside { ChartUtils.drawText( context: context, text: valueText, point: labelPoint, align: align, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: valueTextColor] ) if j < data.entryCount && pe?.label != nil { ChartUtils.drawText( context: context, text: pe!.label!, point: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight), align: align, attributes: [ NSAttributedString.Key.font: entryLabelFont ?? valueFont, NSAttributedString.Key.foregroundColor: entryLabelColor ?? valueTextColor] ) } } else if drawXOutside { if j < data.entryCount && pe?.label != nil { ChartUtils.drawText( context: context, text: pe!.label!, point: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight / 2.0), align: align, attributes: [ NSAttributedString.Key.font: entryLabelFont ?? valueFont, NSAttributedString.Key.foregroundColor: entryLabelColor ?? valueTextColor] ) } } else if drawYOutside { ChartUtils.drawText( context: context, text: valueText, point: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight / 2.0), align: align, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: valueTextColor] ) } } if drawXInside || drawYInside { // calculate the text position let x = labelRadius * sliceXBase + center.x let y = labelRadius * sliceYBase + center.y - lineHeight if drawXInside && drawYInside { ChartUtils.drawText( context: context, text: valueText, point: CGPoint(x: x, y: y), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: valueTextColor] ) if j < data.entryCount && pe?.label != nil { ChartUtils.drawText( context: context, text: pe!.label!, point: CGPoint(x: x, y: y + lineHeight), align: .center, attributes: [ NSAttributedString.Key.font: entryLabelFont ?? valueFont, NSAttributedString.Key.foregroundColor: entryLabelColor ?? valueTextColor] ) } } else if drawXInside { if j < data.entryCount && pe?.label != nil { ChartUtils.drawText( context: context, text: pe!.label!, point: CGPoint(x: x, y: y + lineHeight / 2.0), align: .center, attributes: [ NSAttributedString.Key.font: entryLabelFont ?? valueFont, NSAttributedString.Key.foregroundColor: entryLabelColor ?? valueTextColor] ) } } else if drawYInside { ChartUtils.drawText( context: context, text: valueText, point: CGPoint(x: x, y: y + lineHeight / 2.0), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: valueTextColor] ) } } if let icon = e.icon, dataSet.isDrawIconsEnabled { // calculate the icon's position let x = (labelRadius + iconsOffset.y) * sliceXBase + center.x var y = (labelRadius + iconsOffset.y) * sliceYBase + center.y y += iconsOffset.x ChartUtils.drawImage(context: context, image: icon, x: x, y: y, size: icon.size) } xIndex += 1 } } } open override func drawExtras(context: CGContext) { drawHole(context: context) drawCenterText(context: context) } /// draws the hole in the center of the chart and the transparent circle / hole private func drawHole(context: CGContext) { guard let chart = chart else { return } if chart.drawHoleEnabled { context.saveGState() let radius = chart.radius let holeRadius = radius * chart.holeRadiusPercent let center = chart.centerCircleBox if let holeColor = chart.holeColor { if holeColor != NSUIColor.clear { // draw the hole-circle context.setFillColor(chart.holeColor!.cgColor) context.fillEllipse(in: CGRect(x: center.x - holeRadius, y: center.y - holeRadius, width: holeRadius * 2.0, height: holeRadius * 2.0)) } } // only draw the circle if it can be seen (not covered by the hole) if let transparentCircleColor = chart.transparentCircleColor { if transparentCircleColor != NSUIColor.clear && chart.transparentCircleRadiusPercent > chart.holeRadiusPercent { let alpha = animator.phaseX * animator.phaseY let secondHoleRadius = radius * chart.transparentCircleRadiusPercent // make transparent context.setAlpha(CGFloat(alpha)) context.setFillColor(transparentCircleColor.cgColor) // draw the transparent-circle context.beginPath() context.addEllipse(in: CGRect( x: center.x - secondHoleRadius, y: center.y - secondHoleRadius, width: secondHoleRadius * 2.0, height: secondHoleRadius * 2.0)) context.addEllipse(in: CGRect( x: center.x - holeRadius, y: center.y - holeRadius, width: holeRadius * 2.0, height: holeRadius * 2.0)) context.fillPath(using: .evenOdd) } } context.restoreGState() } } /// draws the description text in the center of the pie chart makes most sense when center-hole is enabled private func drawCenterText(context: CGContext) { guard let chart = chart, let centerAttributedText = chart.centerAttributedText else { return } if chart.drawCenterTextEnabled && centerAttributedText.length > 0 { let center = chart.centerCircleBox let offset = chart.centerTextOffset let innerRadius = chart.drawHoleEnabled && !chart.drawSlicesUnderHoleEnabled ? chart.radius * chart.holeRadiusPercent : chart.radius let x = center.x + offset.x let y = center.y + offset.y let holeRect = CGRect( x: x - innerRadius, y: y - innerRadius, width: innerRadius * 2.0, height: innerRadius * 2.0) var boundingRect = holeRect if chart.centerTextRadiusPercent > 0.0 { boundingRect = boundingRect.insetBy(dx: (boundingRect.width - boundingRect.width * chart.centerTextRadiusPercent) / 2.0, dy: (boundingRect.height - boundingRect.height * chart.centerTextRadiusPercent) / 2.0) } let textBounds = centerAttributedText.boundingRect(with: boundingRect.size, options: [.usesLineFragmentOrigin, .usesFontLeading, .truncatesLastVisibleLine], context: nil) var drawingRect = boundingRect drawingRect.origin.x += (boundingRect.size.width - textBounds.size.width) / 2.0 drawingRect.origin.y += (boundingRect.size.height - textBounds.size.height) / 2.0 drawingRect.size = textBounds.size context.saveGState() let clippingPath = CGPath(ellipseIn: holeRect, transform: nil) context.beginPath() context.addPath(clippingPath) context.clip() centerAttributedText.draw(with: drawingRect, options: [.usesLineFragmentOrigin, .usesFontLeading, .truncatesLastVisibleLine], context: nil) context.restoreGState() } } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let chart = chart, let data = chart.data else { return } context.saveGState() let phaseX = animator.phaseX let phaseY = animator.phaseY var angle: CGFloat = 0.0 let rotationAngle = chart.rotationAngle let drawAngles = chart.drawAngles let absoluteAngles = chart.absoluteAngles let center = chart.centerCircleBox let radius = chart.radius let drawInnerArc = chart.drawHoleEnabled && !chart.drawSlicesUnderHoleEnabled let userInnerRadius = drawInnerArc ? radius * chart.holeRadiusPercent : 0.0 for i in 0 ..< indices.count { // get the index to highlight let index = Int(indices[i].x) if index >= drawAngles.count { continue } guard let set = data.getDataSetByIndex(indices[i].dataSetIndex) as? IPieChartDataSet else { continue } if !set.isHighlightEnabled { continue } let entryCount = set.entryCount var visibleAngleCount = 0 for j in 0 ..< entryCount { guard let e = set.entryForIndex(j) else { continue } if ((abs(e.y) > Double.ulpOfOne)) { visibleAngleCount += 1 } } if index == 0 { angle = 0.0 } else { angle = absoluteAngles[index - 1] * CGFloat(phaseX) } let sliceSpace = visibleAngleCount <= 1 ? 0.0 : set.sliceSpace let sliceAngle = drawAngles[index] var innerRadius = userInnerRadius let shift = set.selectionShift let highlightedRadius = radius + shift let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 context.setFillColor(set.highlightColor?.cgColor ?? set.color(atIndex: index).cgColor) let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : sliceSpace / radius.DEG2RAD let sliceSpaceAngleShifted = visibleAngleCount == 1 ? 0.0 : sliceSpace / highlightedRadius.DEG2RAD let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * CGFloat(phaseY) var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * CGFloat(phaseY) if sweepAngleOuter < 0.0 { sweepAngleOuter = 0.0 } let startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.0) * CGFloat(phaseY) var sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * CGFloat(phaseY) if sweepAngleShifted < 0.0 { sweepAngleShifted = 0.0 } let path = CGMutablePath() path.move(to: CGPoint(x: center.x + highlightedRadius * cos(startAngleShifted.DEG2RAD), y: center.y + highlightedRadius * sin(startAngleShifted.DEG2RAD))) path.addRelativeArc(center: center, radius: highlightedRadius, startAngle: startAngleShifted.DEG2RAD, delta: sweepAngleShifted.DEG2RAD) var sliceSpaceRadius: CGFloat = 0.0 if accountForSliceSpacing { sliceSpaceRadius = calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, angle: sliceAngle * CGFloat(phaseY), arcStartPointX: center.x + radius * cos(startAngleOuter.DEG2RAD), arcStartPointY: center.y + radius * sin(startAngleOuter.DEG2RAD), startAngle: startAngleOuter, sweepAngle: sweepAngleOuter) } if drawInnerArc && (innerRadius > 0.0 || accountForSliceSpacing) { if accountForSliceSpacing { var minSpacedRadius = sliceSpaceRadius if minSpacedRadius < 0.0 { minSpacedRadius = -minSpacedRadius } innerRadius = min(max(innerRadius, minSpacedRadius), radius) } let sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.0 ? 0.0 : sliceSpace / innerRadius.DEG2RAD let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * CGFloat(phaseY) var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * CGFloat(phaseY) if sweepAngleInner < 0.0 { sweepAngleInner = 0.0 } let endAngleInner = startAngleInner + sweepAngleInner path.addLine( to: CGPoint( x: center.x + innerRadius * cos(endAngleInner.DEG2RAD), y: center.y + innerRadius * sin(endAngleInner.DEG2RAD))) path.addRelativeArc(center: center, radius: innerRadius, startAngle: endAngleInner.DEG2RAD, delta: -sweepAngleInner.DEG2RAD) } else { if accountForSliceSpacing { let angleMiddle = startAngleOuter + sweepAngleOuter / 2.0 let arcEndPointX = center.x + sliceSpaceRadius * cos(angleMiddle.DEG2RAD) let arcEndPointY = center.y + sliceSpaceRadius * sin(angleMiddle.DEG2RAD) path.addLine( to: CGPoint( x: arcEndPointX, y: arcEndPointY)) } else { path.addLine(to: center) } } path.closeSubpath() context.beginPath() context.addPath(path) context.fillPath(using: .evenOdd) } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/RadarChartRenderer.swift ================================================ // // RadarChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class RadarChartRenderer: LineRadarRenderer { @objc open weak var chart: RadarChartView? @objc public init(chart: RadarChartView, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.chart = chart } open override func drawData(context: CGContext) { guard let chart = chart else { return } let radarData = chart.data if radarData != nil { let mostEntries = radarData?.maxEntryCountSet?.entryCount ?? 0 for set in radarData!.dataSets as! [IRadarChartDataSet] { if set.isVisible { drawDataSet(context: context, dataSet: set, mostEntries: mostEntries) } } } } /// Draws the RadarDataSet /// /// - parameter context: /// - parameter dataSet: /// - parameter mostEntries: the entry count of the dataset with the most entries internal func drawDataSet(context: CGContext, dataSet: IRadarChartDataSet, mostEntries: Int) { guard let chart = chart else { return } context.saveGState() let phaseX = animator.phaseX let phaseY = animator.phaseY let sliceangle = chart.sliceAngle // calculate the factor that is needed for transforming the value to pixels let factor = chart.factor let center = chart.centerOffsets let entryCount = dataSet.entryCount let path = CGMutablePath() var hasMovedToPoint = false for j in 0 ..< entryCount { guard let e = dataSet.entryForIndex(j) else { continue } let p = center.moving(distance: CGFloat((e.y - chart.chartYMin) * Double(factor) * phaseY), atAngle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) if p.x.isNaN { continue } if !hasMovedToPoint { path.move(to: p) hasMovedToPoint = true } else { path.addLine(to: p) } } // if this is the largest set, close it if dataSet.entryCount < mostEntries { // if this is not the largest set, draw a line to the center before closing path.addLine(to: center) } path.closeSubpath() // draw filled if dataSet.isDrawFilledEnabled { if dataSet.fill != nil { drawFilledPath(context: context, path: path, fill: dataSet.fill!, fillAlpha: dataSet.fillAlpha) } else { drawFilledPath(context: context, path: path, fillColor: dataSet.fillColor, fillAlpha: dataSet.fillAlpha) } } // draw the line (only if filled is disabled or alpha is below 255) if !dataSet.isDrawFilledEnabled || dataSet.fillAlpha < 1.0 { context.setStrokeColor(dataSet.color(atIndex: 0).cgColor) context.setLineWidth(dataSet.lineWidth) context.setAlpha(1.0) context.beginPath() context.addPath(path) context.strokePath() } context.restoreGState() } open override func drawValues(context: CGContext) { guard let chart = chart, let data = chart.data else { return } let phaseX = animator.phaseX let phaseY = animator.phaseY let sliceangle = chart.sliceAngle // calculate the factor that is needed for transforming the value to pixels let factor = chart.factor let center = chart.centerOffsets let yoffset = CGFloat(5.0) for i in 0 ..< data.dataSetCount { let dataSet = data.getDataSetByIndex(i) as! IRadarChartDataSet if !shouldDrawValues(forDataSet: dataSet) { continue } let entryCount = dataSet.entryCount let iconsOffset = dataSet.iconsOffset for j in 0 ..< entryCount { guard let e = dataSet.entryForIndex(j) else { continue } let p = center.moving(distance: CGFloat(e.y - chart.chartYMin) * factor * CGFloat(phaseY), atAngle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) let valueFont = dataSet.valueFont guard let formatter = dataSet.valueFormatter else { continue } if dataSet.isDrawValuesEnabled { ChartUtils.drawText( context: context, text: formatter.stringForValue( e.y, entry: e, dataSetIndex: i, viewPortHandler: viewPortHandler), point: CGPoint(x: p.x, y: p.y - yoffset - valueFont.lineHeight), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: dataSet.valueTextColorAt(j)] ) } if let icon = e.icon, dataSet.isDrawIconsEnabled { var pIcon = center.moving(distance: CGFloat(e.y) * factor * CGFloat(phaseY) + iconsOffset.y, atAngle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) pIcon.y += iconsOffset.x ChartUtils.drawImage(context: context, image: icon, x: pIcon.x, y: pIcon.y, size: icon.size) } } } } open override func drawExtras(context: CGContext) { drawWeb(context: context) } private var _webLineSegmentsBuffer = [CGPoint](repeating: CGPoint(), count: 2) @objc open func drawWeb(context: CGContext) { guard let chart = chart, let data = chart.data else { return } let sliceangle = chart.sliceAngle context.saveGState() // calculate the factor that is needed for transforming the value to // pixels let factor = chart.factor let rotationangle = chart.rotationAngle let center = chart.centerOffsets // draw the web lines that come from the center context.setLineWidth(chart.webLineWidth) context.setStrokeColor(chart.webColor.cgColor) context.setAlpha(chart.webAlpha) let xIncrements = 1 + chart.skipWebLineCount let maxEntryCount = chart.data?.maxEntryCountSet?.entryCount ?? 0 for i in stride(from: 0, to: maxEntryCount, by: xIncrements) { let p = center.moving(distance: CGFloat(chart.yRange) * factor, atAngle: sliceangle * CGFloat(i) + rotationangle) _webLineSegmentsBuffer[0].x = center.x _webLineSegmentsBuffer[0].y = center.y _webLineSegmentsBuffer[1].x = p.x _webLineSegmentsBuffer[1].y = p.y context.strokeLineSegments(between: _webLineSegmentsBuffer) } // draw the inner-web context.setLineWidth(chart.innerWebLineWidth) context.setStrokeColor(chart.innerWebColor.cgColor) context.setAlpha(chart.webAlpha) let labelCount = chart.yAxis.entryCount for j in 0 ..< labelCount { for i in 0 ..< data.entryCount { let r = CGFloat(chart.yAxis.entries[j] - chart.chartYMin) * factor let p1 = center.moving(distance: r, atAngle: sliceangle * CGFloat(i) + rotationangle) let p2 = center.moving(distance: r, atAngle: sliceangle * CGFloat(i + 1) + rotationangle) _webLineSegmentsBuffer[0].x = p1.x _webLineSegmentsBuffer[0].y = p1.y _webLineSegmentsBuffer[1].x = p2.x _webLineSegmentsBuffer[1].y = p2.y context.strokeLineSegments(between: _webLineSegmentsBuffer) } } context.restoreGState() } private var _highlightPointBuffer = CGPoint() open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let chart = chart, let radarData = chart.data as? RadarChartData else { return } context.saveGState() let sliceangle = chart.sliceAngle // calculate the factor that is needed for transforming the value pixels let factor = chart.factor let center = chart.centerOffsets for high in indices { guard let set = chart.data?.getDataSetByIndex(high.dataSetIndex) as? IRadarChartDataSet, set.isHighlightEnabled else { continue } guard let e = set.entryForIndex(Int(high.x)) as? RadarChartDataEntry else { continue } if !isInBoundsX(entry: e, dataSet: set) { continue } context.setLineWidth(radarData.highlightLineWidth) if radarData.highlightLineDashLengths != nil { context.setLineDash(phase: radarData.highlightLineDashPhase, lengths: radarData.highlightLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.setStrokeColor(set.highlightColor.cgColor) let y = e.y - chart.chartYMin _highlightPointBuffer = center.moving(distance: CGFloat(y) * factor * CGFloat(animator.phaseY), atAngle: sliceangle * CGFloat(high.x) * CGFloat(animator.phaseX) + chart.rotationAngle) high.setDraw(pt: _highlightPointBuffer) // draw the lines drawHighlightLines(context: context, point: _highlightPointBuffer, set: set) if set.isDrawHighlightCircleEnabled { if !_highlightPointBuffer.x.isNaN && !_highlightPointBuffer.y.isNaN { var strokeColor = set.highlightCircleStrokeColor if strokeColor == nil { strokeColor = set.color(atIndex: 0) } if set.highlightCircleStrokeAlpha < 1.0 { strokeColor = strokeColor?.withAlphaComponent(set.highlightCircleStrokeAlpha) } drawHighlightCircle( context: context, atPoint: _highlightPointBuffer, innerRadius: set.highlightCircleInnerRadius, outerRadius: set.highlightCircleOuterRadius, fillColor: set.highlightCircleFillColor, strokeColor: strokeColor, strokeWidth: set.highlightCircleStrokeWidth) } } } context.restoreGState() } internal func drawHighlightCircle( context: CGContext, atPoint point: CGPoint, innerRadius: CGFloat, outerRadius: CGFloat, fillColor: NSUIColor?, strokeColor: NSUIColor?, strokeWidth: CGFloat) { context.saveGState() if let fillColor = fillColor { context.beginPath() context.addEllipse(in: CGRect(x: point.x - outerRadius, y: point.y - outerRadius, width: outerRadius * 2.0, height: outerRadius * 2.0)) if innerRadius > 0.0 { context.addEllipse(in: CGRect(x: point.x - innerRadius, y: point.y - innerRadius, width: innerRadius * 2.0, height: innerRadius * 2.0)) } context.setFillColor(fillColor.cgColor) context.fillPath(using: .evenOdd) } if let strokeColor = strokeColor { context.beginPath() context.addEllipse(in: CGRect(x: point.x - outerRadius, y: point.y - outerRadius, width: outerRadius * 2.0, height: outerRadius * 2.0)) context.setStrokeColor(strokeColor.cgColor) context.setLineWidth(strokeWidth) context.strokePath() } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Renderer.swift ================================================ // // Renderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartRenderer) open class Renderer: NSObject { /// the component that handles the drawing area of the chart and it's offsets @objc public let viewPortHandler: ViewPortHandler @objc public init(viewPortHandler: ViewPortHandler) { self.viewPortHandler = viewPortHandler super.init() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/ChevronDownShapeRenderer.swift ================================================ // // ChevronDownShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ChevronDownShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 context.setLineWidth(1.0) context.setStrokeColor(color.cgColor) context.beginPath() context.move(to: CGPoint(x: point.x, y: point.y + 2 * shapeHalf)) context.addLine(to: CGPoint(x: point.x + 2 * shapeHalf, y: point.y)) context.move(to: CGPoint(x: point.x, y: point.y + 2 * shapeHalf)) context.addLine(to: CGPoint(x: point.x - 2 * shapeHalf, y: point.y)) context.strokePath() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/ChevronUpShapeRenderer.swift ================================================ // // ChevronUpShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class ChevronUpShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 context.setLineWidth(1.0) context.setStrokeColor(color.cgColor) context.beginPath() context.move(to: CGPoint(x: point.x, y: point.y - 2 * shapeHalf)) context.addLine(to: CGPoint(x: point.x + 2 * shapeHalf, y: point.y)) context.move(to: CGPoint(x: point.x, y: point.y - 2 * shapeHalf)) context.addLine(to: CGPoint(x: point.x - 2 * shapeHalf, y: point.y)) context.strokePath() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/CircleShapeRenderer.swift ================================================ // // CircleShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class CircleShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 let shapeHoleSizeHalf = dataSet.scatterShapeHoleRadius let shapeHoleSize = shapeHoleSizeHalf * 2.0 let shapeHoleColor = dataSet.scatterShapeHoleColor let shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.0 let shapeStrokeSizeHalf = shapeStrokeSize / 2.0 if shapeHoleSize > 0.0 { context.setStrokeColor(color.cgColor) context.setLineWidth(shapeStrokeSize) var rect = CGRect() rect.origin.x = point.x - shapeHoleSizeHalf - shapeStrokeSizeHalf rect.origin.y = point.y - shapeHoleSizeHalf - shapeStrokeSizeHalf rect.size.width = shapeHoleSize + shapeStrokeSize rect.size.height = shapeHoleSize + shapeStrokeSize context.strokeEllipse(in: rect) if let shapeHoleColor = shapeHoleColor { context.setFillColor(shapeHoleColor.cgColor) rect.origin.x = point.x - shapeHoleSizeHalf rect.origin.y = point.y - shapeHoleSizeHalf rect.size.width = shapeHoleSize rect.size.height = shapeHoleSize context.fillEllipse(in: rect) } } else { context.setFillColor(color.cgColor) var rect = CGRect() rect.origin.x = point.x - shapeHalf rect.origin.y = point.y - shapeHalf rect.size.width = shapeSize rect.size.height = shapeSize context.fillEllipse(in: rect) } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/CrossShapeRenderer.swift ================================================ // // CrossShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class CrossShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 context.setLineWidth(1.0) context.setStrokeColor(color.cgColor) context.beginPath() context.move(to: CGPoint(x: point.x - shapeHalf, y: point.y)) context.addLine(to: CGPoint(x: point.x + shapeHalf, y: point.y)) context.move(to: CGPoint(x: point.x, y: point.y - shapeHalf)) context.addLine(to: CGPoint(x: point.x, y: point.y + shapeHalf)) context.strokePath() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/IShapeRenderer.swift ================================================ // // IShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc public protocol IShapeRenderer: class { /// Renders the provided ScatterDataSet with a shape. /// /// - parameter context: CGContext for drawing on /// - parameter dataSet: The DataSet to be drawn /// - parameter viewPortHandler: Contains information about the current state of the view /// - parameter point: Position to draw the shape at /// - parameter color: Color to draw the shape func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/SquareShapeRenderer.swift ================================================ // // SquareShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class SquareShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 let shapeHoleSizeHalf = dataSet.scatterShapeHoleRadius let shapeHoleSize = shapeHoleSizeHalf * 2.0 let shapeHoleColor = dataSet.scatterShapeHoleColor let shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.0 let shapeStrokeSizeHalf = shapeStrokeSize / 2.0 if shapeHoleSize > 0.0 { context.setStrokeColor(color.cgColor) context.setLineWidth(shapeStrokeSize) var rect = CGRect() rect.origin.x = point.x - shapeHoleSizeHalf - shapeStrokeSizeHalf rect.origin.y = point.y - shapeHoleSizeHalf - shapeStrokeSizeHalf rect.size.width = shapeHoleSize + shapeStrokeSize rect.size.height = shapeHoleSize + shapeStrokeSize context.stroke(rect) if let shapeHoleColor = shapeHoleColor { context.setFillColor(shapeHoleColor.cgColor) rect.origin.x = point.x - shapeHoleSizeHalf rect.origin.y = point.y - shapeHoleSizeHalf rect.size.width = shapeHoleSize rect.size.height = shapeHoleSize context.fill(rect) } } else { context.setFillColor(color.cgColor) var rect = CGRect() rect.origin.x = point.x - shapeHalf rect.origin.y = point.y - shapeHalf rect.size.width = shapeSize rect.size.height = shapeSize context.fill(rect) } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/TriangleShapeRenderer.swift ================================================ // // TriangleShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class TriangleShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 let shapeHoleSizeHalf = dataSet.scatterShapeHoleRadius let shapeHoleSize = shapeHoleSizeHalf * 2.0 let shapeHoleColor = dataSet.scatterShapeHoleColor let shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.0 context.setFillColor(color.cgColor) // create a triangle path context.beginPath() context.move(to: CGPoint(x: point.x, y: point.y - shapeHalf)) context.addLine(to: CGPoint(x: point.x + shapeHalf, y: point.y + shapeHalf)) context.addLine(to: CGPoint(x: point.x - shapeHalf, y: point.y + shapeHalf)) if shapeHoleSize > 0.0 { context.addLine(to: CGPoint(x: point.x, y: point.y - shapeHalf)) context.move(to: CGPoint(x: point.x - shapeHalf + shapeStrokeSize, y: point.y + shapeHalf - shapeStrokeSize)) context.addLine(to: CGPoint(x: point.x + shapeHalf - shapeStrokeSize, y: point.y + shapeHalf - shapeStrokeSize)) context.addLine(to: CGPoint(x: point.x, y: point.y - shapeHalf + shapeStrokeSize)) context.addLine(to: CGPoint(x: point.x - shapeHalf + shapeStrokeSize, y: point.y + shapeHalf - shapeStrokeSize)) } context.closePath() context.fillPath() if shapeHoleSize > 0.0 && shapeHoleColor != nil { context.setFillColor(shapeHoleColor!.cgColor) // create a triangle path context.beginPath() context.move(to: CGPoint(x: point.x, y: point.y - shapeHalf + shapeStrokeSize)) context.addLine(to: CGPoint(x: point.x + shapeHalf - shapeStrokeSize, y: point.y + shapeHalf - shapeStrokeSize)) context.addLine(to: CGPoint(x: point.x - shapeHalf + shapeStrokeSize, y: point.y + shapeHalf - shapeStrokeSize)) context.closePath() context.fillPath() } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/Scatter/XShapeRenderer.swift ================================================ // // XShapeRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics open class XShapeRenderer : NSObject, IShapeRenderer { open func renderShape( context: CGContext, dataSet: IScatterChartDataSet, viewPortHandler: ViewPortHandler, point: CGPoint, color: NSUIColor) { let shapeSize = dataSet.scatterShapeSize let shapeHalf = shapeSize / 2.0 context.setLineWidth(1.0) context.setStrokeColor(color.cgColor) context.beginPath() context.move(to: CGPoint(x: point.x - shapeHalf, y: point.y - shapeHalf)) context.addLine(to: CGPoint(x: point.x + shapeHalf, y: point.y + shapeHalf)) context.move(to: CGPoint(x: point.x + shapeHalf, y: point.y - shapeHalf)) context.addLine(to: CGPoint(x: point.x - shapeHalf, y: point.y + shapeHalf)) context.strokePath() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/ScatterChartRenderer.swift ================================================ // // ScatterChartRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class ScatterChartRenderer: LineScatterCandleRadarRenderer { @objc open weak var dataProvider: ScatterChartDataProvider? @objc public init(dataProvider: ScatterChartDataProvider, animator: Animator, viewPortHandler: ViewPortHandler) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.dataProvider = dataProvider } open override func drawData(context: CGContext) { guard let scatterData = dataProvider?.scatterData else { return } for i in 0 ..< scatterData.dataSetCount { guard let set = scatterData.getDataSetByIndex(i) else { continue } if set.isVisible { if !(set is IScatterChartDataSet) { fatalError("Datasets for ScatterChartRenderer must conform to IScatterChartDataSet") } drawDataSet(context: context, dataSet: set as! IScatterChartDataSet) } } } private var _lineSegments = [CGPoint](repeating: CGPoint(), count: 2) @objc open func drawDataSet(context: CGContext, dataSet: IScatterChartDataSet) { guard let dataProvider = dataProvider else { return } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let phaseY = animator.phaseY let entryCount = dataSet.entryCount var point = CGPoint() let valueToPixelMatrix = trans.valueToPixelMatrix if let renderer = dataSet.shapeRenderer { context.saveGState() for j in 0 ..< Int(min(ceil(Double(entryCount) * animator.phaseX), Double(entryCount))) { guard let e = dataSet.entryForIndex(j) else { continue } point.x = CGFloat(e.x) point.y = CGFloat(e.y * phaseY) point = point.applying(valueToPixelMatrix) if !viewPortHandler.isInBoundsRight(point.x) { break } if !viewPortHandler.isInBoundsLeft(point.x) || !viewPortHandler.isInBoundsY(point.y) { continue } renderer.renderShape(context: context, dataSet: dataSet, viewPortHandler: viewPortHandler, point: point, color: dataSet.color(atIndex: j)) } context.restoreGState() } else { print("There's no IShapeRenderer specified for ScatterDataSet", terminator: "\n") } } open override func drawValues(context: CGContext) { guard let dataProvider = dataProvider, let scatterData = dataProvider.scatterData else { return } // if values are drawn if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataSets = scatterData.dataSets as? [IScatterChartDataSet] else { return } let phaseY = animator.phaseY var pt = CGPoint() for i in 0 ..< scatterData.dataSetCount { let dataSet = dataSets[i] if !shouldDrawValues(forDataSet: dataSet) { continue } let valueFont = dataSet.valueFont guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(forAxis: dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix let iconsOffset = dataSet.iconsOffset let shapeSize = dataSet.scatterShapeSize let lineHeight = valueFont.lineHeight _xBounds.set(chart: dataProvider, dataSet: dataSet, animator: animator) for j in stride(from: _xBounds.min, through: _xBounds.range + _xBounds.min, by: 1) { guard let e = dataSet.entryForIndex(j) else { break } pt.x = CGFloat(e.x) pt.y = CGFloat(e.y * phaseY) pt = pt.applying(valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) { break } // make sure the lines don't do shitty things outside bounds if (!viewPortHandler.isInBoundsLeft(pt.x) || !viewPortHandler.isInBoundsY(pt.y)) { continue } let text = formatter.stringForValue( e.y, entry: e, dataSetIndex: i, viewPortHandler: viewPortHandler) if dataSet.isDrawValuesEnabled { ChartUtils.drawText( context: context, text: text, point: CGPoint( x: pt.x, y: pt.y - shapeSize - lineHeight), align: .center, attributes: [NSAttributedString.Key.font: valueFont, NSAttributedString.Key.foregroundColor: dataSet.valueTextColorAt(j)] ) } if let icon = e.icon, dataSet.isDrawIconsEnabled { ChartUtils.drawImage(context: context, image: icon, x: pt.x + iconsOffset.x, y: pt.y + iconsOffset.y, size: icon.size) } } } } } open override func drawExtras(context: CGContext) { } open override func drawHighlighted(context: CGContext, indices: [Highlight]) { guard let dataProvider = dataProvider, let scatterData = dataProvider.scatterData else { return } context.saveGState() for high in indices { guard let set = scatterData.getDataSetByIndex(high.dataSetIndex) as? IScatterChartDataSet, set.isHighlightEnabled else { continue } guard let entry = set.entryForXValue(high.x, closestToY: high.y) else { continue } if !isInBoundsX(entry: entry, dataSet: set) { continue } context.setStrokeColor(set.highlightColor.cgColor) context.setLineWidth(set.highlightLineWidth) if set.highlightLineDashLengths != nil { context.setLineDash(phase: set.highlightLineDashPhase, lengths: set.highlightLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } let x = entry.x // get the x-position let y = entry.y * Double(animator.phaseY) let trans = dataProvider.getTransformer(forAxis: set.axisDependency) let pt = trans.pixelForValues(x: x, y: y) high.setDraw(pt: pt) // draw the lines drawHighlightLines(context: context, point: pt, set: set) } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/XAxisRenderer.swift ================================================ // // XAxisRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartXAxisRenderer) open class XAxisRenderer: AxisRendererBase { @objc public init(viewPortHandler: ViewPortHandler, xAxis: XAxis?, transformer: Transformer?) { super.init(viewPortHandler: viewPortHandler, transformer: transformer, axis: xAxis) } open override func computeAxis(min: Double, max: Double, inverted: Bool) { var min = min, max = max if let transformer = self.transformer { // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if viewPortHandler.contentWidth > 10 && !viewPortHandler.isFullyZoomedOutX { let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) if inverted { min = Double(p2.x) max = Double(p1.x) } else { min = Double(p1.x) max = Double(p2.x) } } } computeAxisValues(min: min, max: max) } open override func computeAxisValues(min: Double, max: Double) { super.computeAxisValues(min: min, max: max) computeSize() } @objc open func computeSize() { guard let xAxis = self.axis as? XAxis else { return } let longest = xAxis.getLongestLabel() let labelSize = longest.size(withAttributes: [NSAttributedString.Key.font: xAxis.labelFont]) let labelWidth = labelSize.width let labelHeight = labelSize.height let labelRotatedSize = labelSize.rotatedBy(degrees: xAxis.labelRotationAngle) xAxis.labelWidth = labelWidth xAxis.labelHeight = labelHeight xAxis.labelRotatedWidth = labelRotatedSize.width xAxis.labelRotatedHeight = labelRotatedSize.height } open override func renderAxisLabels(context: CGContext) { guard let xAxis = self.axis as? XAxis else { return } if !xAxis.isEnabled || !xAxis.isDrawLabelsEnabled { return } let yOffset = xAxis.yOffset if xAxis.labelPosition == .top { drawLabels(context: context, pos: viewPortHandler.contentTop - yOffset, anchor: CGPoint(x: 0.5, y: 1.0)) } else if xAxis.labelPosition == .topInside { drawLabels(context: context, pos: viewPortHandler.contentTop + yOffset + xAxis.labelRotatedHeight, anchor: CGPoint(x: 0.5, y: 1.0)) } else if xAxis.labelPosition == .bottom { drawLabels(context: context, pos: viewPortHandler.contentBottom + yOffset, anchor: CGPoint(x: 0.5, y: 0.0)) } else if xAxis.labelPosition == .bottomInside { drawLabels(context: context, pos: viewPortHandler.contentBottom - yOffset - xAxis.labelRotatedHeight, anchor: CGPoint(x: 0.5, y: 0.0)) } else { // BOTH SIDED drawLabels(context: context, pos: viewPortHandler.contentTop - yOffset, anchor: CGPoint(x: 0.5, y: 1.0)) drawLabels(context: context, pos: viewPortHandler.contentBottom + yOffset, anchor: CGPoint(x: 0.5, y: 0.0)) } } private var _axisLineSegmentsBuffer = [CGPoint](repeating: CGPoint(), count: 2) open override func renderAxisLine(context: CGContext) { guard let xAxis = self.axis as? XAxis else { return } if !xAxis.isEnabled || !xAxis.isDrawAxisLineEnabled { return } context.saveGState() context.setStrokeColor(xAxis.axisLineColor.cgColor) context.setLineWidth(xAxis.axisLineWidth) if xAxis.axisLineDashLengths != nil { context.setLineDash(phase: xAxis.axisLineDashPhase, lengths: xAxis.axisLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } if xAxis.labelPosition == .top || xAxis.labelPosition == .topInside || xAxis.labelPosition == .bothSided { _axisLineSegmentsBuffer[0].x = viewPortHandler.contentLeft _axisLineSegmentsBuffer[0].y = viewPortHandler.contentTop _axisLineSegmentsBuffer[1].x = viewPortHandler.contentRight _axisLineSegmentsBuffer[1].y = viewPortHandler.contentTop context.strokeLineSegments(between: _axisLineSegmentsBuffer) } if xAxis.labelPosition == .bottom || xAxis.labelPosition == .bottomInside || xAxis.labelPosition == .bothSided { _axisLineSegmentsBuffer[0].x = viewPortHandler.contentLeft _axisLineSegmentsBuffer[0].y = viewPortHandler.contentBottom _axisLineSegmentsBuffer[1].x = viewPortHandler.contentRight _axisLineSegmentsBuffer[1].y = viewPortHandler.contentBottom context.strokeLineSegments(between: _axisLineSegmentsBuffer) } context.restoreGState() } /// draws the x-labels on the specified y-position @objc open func drawLabels(context: CGContext, pos: CGFloat, anchor: CGPoint) { guard let xAxis = self.axis as? XAxis, let transformer = self.transformer else { return } #if os(OSX) let paraStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle #else let paraStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle #endif paraStyle.alignment = .center let labelAttrs: [NSAttributedString.Key : Any] = [NSAttributedString.Key.font: xAxis.labelFont, NSAttributedString.Key.foregroundColor: xAxis.labelTextColor, NSAttributedString.Key.paragraphStyle: paraStyle] let labelRotationAngleRadians = xAxis.labelRotationAngle.DEG2RAD let centeringEnabled = xAxis.isCenterAxisLabelsEnabled let valueToPixelMatrix = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) var labelMaxSize = CGSize() if xAxis.isWordWrapEnabled { labelMaxSize.width = xAxis.wordWrapWidthPercent * valueToPixelMatrix.a } let entries = xAxis.entries for i in stride(from: 0, to: entries.count, by: 1) { if centeringEnabled { position.x = CGFloat(xAxis.centeredEntries[i]) } else { position.x = CGFloat(entries[i]) } position.y = 0.0 position = position.applying(valueToPixelMatrix) if viewPortHandler.isInBoundsX(position.x) { let label = xAxis.valueFormatter?.stringForValue(xAxis.entries[i], axis: xAxis) ?? "" let labelns = label as NSString if xAxis.isAvoidFirstLastClippingEnabled { // avoid clipping of the last if i == xAxis.entryCount - 1 && xAxis.entryCount > 1 { let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width if width > viewPortHandler.offsetRight * 2.0 && position.x + width > viewPortHandler.chartWidth { position.x -= width / 2.0 } } else if i == 0 { // avoid clipping of the first let width = labelns.boundingRect(with: labelMaxSize, options: .usesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width position.x += width / 2.0 } } drawLabel(context: context, formattedLabel: label, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) } } } @objc open func drawLabel( context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [NSAttributedString.Key : Any], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) { ChartUtils.drawMultilineText( context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) } open override func renderGridLines(context: CGContext) { guard let xAxis = self.axis as? XAxis, let transformer = self.transformer else { return } if !xAxis.isDrawGridLinesEnabled || !xAxis.isEnabled { return } context.saveGState() defer { context.restoreGState() } context.clip(to: self.gridClippingRect) context.setShouldAntialias(xAxis.gridAntialiasEnabled) context.setStrokeColor(xAxis.gridColor.cgColor) context.setLineWidth(xAxis.gridLineWidth) context.setLineCap(xAxis.gridLineCap) if xAxis.gridLineDashLengths != nil { context.setLineDash(phase: xAxis.gridLineDashPhase, lengths: xAxis.gridLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } let valueToPixelMatrix = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) let entries = xAxis.entries for i in stride(from: 0, to: entries.count, by: 1) { position.x = CGFloat(entries[i]) position.y = position.x position = position.applying(valueToPixelMatrix) drawGridLine(context: context, x: position.x, y: position.y) } } @objc open var gridClippingRect: CGRect { var contentRect = viewPortHandler.contentRect let dx = self.axis?.gridLineWidth ?? 0.0 contentRect.origin.x -= dx / 2.0 contentRect.size.width += dx return contentRect } @objc open func drawGridLine(context: CGContext, x: CGFloat, y: CGFloat) { if x >= viewPortHandler.offsetLeft && x <= viewPortHandler.chartWidth { context.beginPath() context.move(to: CGPoint(x: x, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: x, y: viewPortHandler.contentBottom)) context.strokePath() } } open override func renderLimitLines(context: CGContext) { guard let xAxis = self.axis as? XAxis, let transformer = self.transformer else { return } var limitLines = xAxis.limitLines if limitLines.count == 0 { return } let trans = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) for i in 0 ..< limitLines.count { let l = limitLines[i] if !l.isEnabled { continue } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.x -= l.lineWidth / 2.0 clippingRect.size.width += l.lineWidth context.clip(to: clippingRect) position.x = CGFloat(l.limit) position.y = 0.0 position = position.applying(trans) renderLimitLineLine(context: context, limitLine: l, position: position) renderLimitLineLabel(context: context, limitLine: l, position: position, yOffset: 2.0 + l.yOffset) } } @objc open func renderLimitLineLine(context: CGContext, limitLine: ChartLimitLine, position: CGPoint) { context.beginPath() context.move(to: CGPoint(x: position.x, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: position.x, y: viewPortHandler.contentBottom)) context.setStrokeColor(limitLine.lineColor.cgColor) context.setLineWidth(limitLine.lineWidth) if limitLine.lineDashLengths != nil { context.setLineDash(phase: limitLine.lineDashPhase, lengths: limitLine.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.strokePath() } @objc open func renderLimitLineLabel(context: CGContext, limitLine: ChartLimitLine, position: CGPoint, yOffset: CGFloat) { let label = limitLine.label // if drawing the limit-value label is enabled if limitLine.drawLabelEnabled && label.count > 0 { let labelLineHeight = limitLine.valueFont.lineHeight let xOffset: CGFloat = limitLine.lineWidth + limitLine.xOffset if limitLine.labelPosition == .rightTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x + xOffset, y: viewPortHandler.contentTop + yOffset), align: .left, attributes: [NSAttributedString.Key.font: limitLine.valueFont, NSAttributedString.Key.foregroundColor: limitLine.valueTextColor]) } else if limitLine.labelPosition == .rightBottom { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x + xOffset, y: viewPortHandler.contentBottom - labelLineHeight - yOffset), align: .left, attributes: [NSAttributedString.Key.font: limitLine.valueFont, NSAttributedString.Key.foregroundColor: limitLine.valueTextColor]) } else if limitLine.labelPosition == .leftTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x - xOffset, y: viewPortHandler.contentTop + yOffset), align: .right, attributes: [NSAttributedString.Key.font: limitLine.valueFont, NSAttributedString.Key.foregroundColor: limitLine.valueTextColor]) } else { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x - xOffset, y: viewPortHandler.contentBottom - labelLineHeight - yOffset), align: .right, attributes: [NSAttributedString.Key.font: limitLine.valueFont, NSAttributedString.Key.foregroundColor: limitLine.valueTextColor]) } } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/XAxisRendererHorizontalBarChart.swift ================================================ // // XAxisRendererHorizontalBarChart.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class XAxisRendererHorizontalBarChart: XAxisRenderer { internal weak var chart: BarChartView? @objc public init(viewPortHandler: ViewPortHandler, xAxis: XAxis?, transformer: Transformer?, chart: BarChartView) { super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer) self.chart = chart } open override func computeAxis(min: Double, max: Double, inverted: Bool) { var min = min, max = max if let transformer = self.transformer { // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if viewPortHandler.contentWidth > 10 && !viewPortHandler.isFullyZoomedOutY { let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) if inverted { min = Double(p2.y) max = Double(p1.y) } else { min = Double(p1.y) max = Double(p2.y) } } } computeAxisValues(min: min, max: max) } open override func computeSize() { guard let xAxis = self.axis as? XAxis else { return } let longest = xAxis.getLongestLabel() as NSString let labelSize = longest.size(withAttributes: [NSAttributedString.Key.font: xAxis.labelFont]) let labelWidth = floor(labelSize.width + xAxis.xOffset * 3.5) let labelHeight = labelSize.height let labelRotatedSize = CGSize(width: labelSize.width, height: labelHeight).rotatedBy(degrees: xAxis.labelRotationAngle) xAxis.labelWidth = labelWidth xAxis.labelHeight = labelHeight xAxis.labelRotatedWidth = round(labelRotatedSize.width + xAxis.xOffset * 3.5) xAxis.labelRotatedHeight = round(labelRotatedSize.height) } open override func renderAxisLabels(context: CGContext) { guard let xAxis = self.axis as? XAxis else { return } if !xAxis.isEnabled || !xAxis.isDrawLabelsEnabled || chart?.data === nil { return } let xoffset = xAxis.xOffset if xAxis.labelPosition == .top { drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) } else if xAxis.labelPosition == .topInside { drawLabels(context: context, pos: viewPortHandler.contentRight - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } else if xAxis.labelPosition == .bottom { drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } else if xAxis.labelPosition == .bottomInside { drawLabels(context: context, pos: viewPortHandler.contentLeft + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) } else { // BOTH SIDED drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, anchor: CGPoint(x: 0.0, y: 0.5)) drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, anchor: CGPoint(x: 1.0, y: 0.5)) } } /// draws the x-labels on the specified y-position open override func drawLabels(context: CGContext, pos: CGFloat, anchor: CGPoint) { guard let xAxis = self.axis as? XAxis, let transformer = self.transformer else { return } let labelFont = xAxis.labelFont let labelTextColor = xAxis.labelTextColor let labelRotationAngleRadians = xAxis.labelRotationAngle.DEG2RAD let centeringEnabled = xAxis.isCenterAxisLabelsEnabled // pre allocate to save performance (dont allocate in loop) var position = CGPoint(x: 0.0, y: 0.0) for i in stride(from: 0, to: xAxis.entryCount, by: 1) { // only fill x values position.x = 0.0 if centeringEnabled { position.y = CGFloat(xAxis.centeredEntries[i]) } else { position.y = CGFloat(xAxis.entries[i]) } transformer.pointValueToPixel(&position) if viewPortHandler.isInBoundsY(position.y) { if let label = xAxis.valueFormatter?.stringForValue(xAxis.entries[i], axis: xAxis) { drawLabel( context: context, formattedLabel: label, x: pos, y: position.y, attributes: [NSAttributedString.Key.font: labelFont, NSAttributedString.Key.foregroundColor: labelTextColor], anchor: anchor, angleRadians: labelRotationAngleRadians) } } } } @objc open func drawLabel( context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [NSAttributedString.Key : Any], anchor: CGPoint, angleRadians: CGFloat) { ChartUtils.drawText( context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) } open override var gridClippingRect: CGRect { var contentRect = viewPortHandler.contentRect let dy = self.axis?.gridLineWidth ?? 0.0 contentRect.origin.y -= dy / 2.0 contentRect.size.height += dy return contentRect } private var _gridLineSegmentsBuffer = [CGPoint](repeating: CGPoint(), count: 2) open override func drawGridLine(context: CGContext, x: CGFloat, y: CGFloat) { if viewPortHandler.isInBoundsY(y) { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: y)) context.strokePath() } } open override func renderAxisLine(context: CGContext) { guard let xAxis = self.axis as? XAxis else { return } if !xAxis.isEnabled || !xAxis.isDrawAxisLineEnabled { return } context.saveGState() context.setStrokeColor(xAxis.axisLineColor.cgColor) context.setLineWidth(xAxis.axisLineWidth) if xAxis.axisLineDashLengths != nil { context.setLineDash(phase: xAxis.axisLineDashPhase, lengths: xAxis.axisLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } if xAxis.labelPosition == .top || xAxis.labelPosition == .topInside || xAxis.labelPosition == .bothSided { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentBottom)) context.strokePath() } if xAxis.labelPosition == .bottom || xAxis.labelPosition == .bottomInside || xAxis.labelPosition == .bothSided { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) context.strokePath() } context.restoreGState() } open override func renderLimitLines(context: CGContext) { guard let xAxis = self.axis as? XAxis, let transformer = self.transformer else { return } var limitLines = xAxis.limitLines if limitLines.count == 0 { return } let trans = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) for i in 0 ..< limitLines.count { let l = limitLines[i] if !l.isEnabled { continue } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.y -= l.lineWidth / 2.0 clippingRect.size.height += l.lineWidth context.clip(to: clippingRect) position.x = 0.0 position.y = CGFloat(l.limit) position = position.applying(trans) context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: position.y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: position.y)) context.setStrokeColor(l.lineColor.cgColor) context.setLineWidth(l.lineWidth) if l.lineDashLengths != nil { context.setLineDash(phase: l.lineDashPhase, lengths: l.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.strokePath() let label = l.label // if drawing the limit-value label is enabled if l.drawLabelEnabled && label.count > 0 { let labelLineHeight = l.valueFont.lineHeight let xOffset: CGFloat = 4.0 + l.xOffset let yOffset: CGFloat = l.lineWidth + labelLineHeight + l.yOffset if l.labelPosition == .rightTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentRight - xOffset, y: position.y - yOffset), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .rightBottom { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentRight - xOffset, y: position.y + yOffset - labelLineHeight), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .leftTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentLeft + xOffset, y: position.y - yOffset), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentLeft + xOffset, y: position.y + yOffset - labelLineHeight), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } } } } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/XAxisRendererRadarChart.swift ================================================ // // XAxisRendererRadarChart.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class XAxisRendererRadarChart: XAxisRenderer { @objc open weak var chart: RadarChartView? @objc public init(viewPortHandler: ViewPortHandler, xAxis: XAxis?, chart: RadarChartView) { super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: nil) self.chart = chart } open override func renderAxisLabels(context: CGContext) { guard let xAxis = axis as? XAxis, let chart = chart else { return } if !xAxis.isEnabled || !xAxis.isDrawLabelsEnabled { return } let labelFont = xAxis.labelFont let labelTextColor = xAxis.labelTextColor let labelRotationAngleRadians = xAxis.labelRotationAngle.RAD2DEG let drawLabelAnchor = CGPoint(x: 0.5, y: 0.25) let sliceangle = chart.sliceAngle // calculate the factor that is needed for transforming the value to pixels let factor = chart.factor let center = chart.centerOffsets for i in stride(from: 0, to: chart.data?.maxEntryCountSet?.entryCount ?? 0, by: 1) { let label = xAxis.valueFormatter?.stringForValue(Double(i), axis: xAxis) ?? "" let angle = (sliceangle * CGFloat(i) + chart.rotationAngle).truncatingRemainder(dividingBy: 360.0) let p = center.moving(distance: CGFloat(chart.yRange) * factor + xAxis.labelRotatedWidth / 2.0, atAngle: angle) drawLabel(context: context, formattedLabel: label, x: p.x, y: p.y - xAxis.labelRotatedHeight / 2.0, attributes: [NSAttributedString.Key.font: labelFont, NSAttributedString.Key.foregroundColor: labelTextColor], anchor: drawLabelAnchor, angleRadians: labelRotationAngleRadians) } } @objc open func drawLabel( context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [NSAttributedString.Key : Any], anchor: CGPoint, angleRadians: CGFloat) { ChartUtils.drawText( context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) } open override func renderLimitLines(context: CGContext) { /// XAxis LimitLines on RadarChart not yet supported. } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/YAxisRenderer.swift ================================================ // // YAxisRenderer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif @objc(ChartYAxisRenderer) open class YAxisRenderer: AxisRendererBase { @objc public init(viewPortHandler: ViewPortHandler, yAxis: YAxis?, transformer: Transformer?) { super.init(viewPortHandler: viewPortHandler, transformer: transformer, axis: yAxis) } /// draws the y-axis labels to the screen open override func renderAxisLabels(context: CGContext) { guard let yAxis = self.axis as? YAxis else { return } if !yAxis.isEnabled || !yAxis.isDrawLabelsEnabled { return } let xoffset = yAxis.xOffset let yoffset = yAxis.labelFont.lineHeight / 2.5 + yAxis.yOffset let dependency = yAxis.axisDependency let labelPosition = yAxis.labelPosition var xPos = CGFloat(0.0) var textAlign: NSTextAlignment if dependency == .left { if labelPosition == .outsideChart { textAlign = .right xPos = viewPortHandler.offsetLeft - xoffset } else { textAlign = .left xPos = viewPortHandler.offsetLeft + xoffset } } else { if labelPosition == .outsideChart { textAlign = .left xPos = viewPortHandler.contentRight + xoffset } else { textAlign = .right xPos = viewPortHandler.contentRight - xoffset } } drawYLabels( context: context, fixedPosition: xPos, positions: transformedPositions(), offset: yoffset - yAxis.labelFont.lineHeight, textAlign: textAlign) } open override func renderAxisLine(context: CGContext) { guard let yAxis = self.axis as? YAxis else { return } if !yAxis.isEnabled || !yAxis.drawAxisLineEnabled { return } context.saveGState() context.setStrokeColor(yAxis.axisLineColor.cgColor) context.setLineWidth(yAxis.axisLineWidth) if yAxis.axisLineDashLengths != nil { context.setLineDash(phase: yAxis.axisLineDashPhase, lengths: yAxis.axisLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } if yAxis.axisDependency == .left { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) context.strokePath() } else { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentBottom)) context.strokePath() } context.restoreGState() } /// draws the y-labels on the specified x-position internal func drawYLabels( context: CGContext, fixedPosition: CGFloat, positions: [CGPoint], offset: CGFloat, textAlign: NSTextAlignment) { guard let yAxis = self.axis as? YAxis else { return } let labelFont = yAxis.labelFont let labelTextColor = yAxis.labelTextColor let from = yAxis.isDrawBottomYLabelEntryEnabled ? 0 : 1 let to = yAxis.isDrawTopYLabelEntryEnabled ? yAxis.entryCount : (yAxis.entryCount - 1) for i in stride(from: from, to: to, by: 1) { let text = yAxis.getFormattedLabel(i) ChartUtils.drawText( context: context, text: text, point: CGPoint(x: fixedPosition, y: positions[i].y + offset), align: textAlign, attributes: [NSAttributedString.Key.font: labelFont, NSAttributedString.Key.foregroundColor: labelTextColor]) } } open override func renderGridLines(context: CGContext) { guard let yAxis = self.axis as? YAxis else { return } if !yAxis.isEnabled { return } if yAxis.drawGridLinesEnabled { let positions = transformedPositions() context.saveGState() defer { context.restoreGState() } context.clip(to: self.gridClippingRect) context.setShouldAntialias(yAxis.gridAntialiasEnabled) context.setStrokeColor(yAxis.gridColor.cgColor) context.setLineWidth(yAxis.gridLineWidth) context.setLineCap(yAxis.gridLineCap) if yAxis.gridLineDashLengths != nil { context.setLineDash(phase: yAxis.gridLineDashPhase, lengths: yAxis.gridLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } // draw the grid for i in 0 ..< positions.count { drawGridLine(context: context, position: positions[i]) } } if yAxis.drawZeroLineEnabled { // draw zero line drawZeroLine(context: context) } } @objc open var gridClippingRect: CGRect { var contentRect = viewPortHandler.contentRect let dy = self.axis?.gridLineWidth ?? 0.0 contentRect.origin.y -= dy / 2.0 contentRect.size.height += dy return contentRect } @objc open func drawGridLine( context: CGContext, position: CGPoint) { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: position.y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: position.y)) context.strokePath() } @objc open func transformedPositions() -> [CGPoint] { guard let yAxis = self.axis as? YAxis, let transformer = self.transformer else { return [CGPoint]() } var positions = [CGPoint]() positions.reserveCapacity(yAxis.entryCount) let entries = yAxis.entries for i in stride(from: 0, to: yAxis.entryCount, by: 1) { positions.append(CGPoint(x: 0.0, y: entries[i])) } transformer.pointValuesToPixel(&positions) return positions } /// Draws the zero line at the specified position. @objc open func drawZeroLine(context: CGContext) { guard let yAxis = self.axis as? YAxis, let transformer = self.transformer, let zeroLineColor = yAxis.zeroLineColor else { return } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.y -= yAxis.zeroLineWidth / 2.0 clippingRect.size.height += yAxis.zeroLineWidth context.clip(to: clippingRect) context.setStrokeColor(zeroLineColor.cgColor) context.setLineWidth(yAxis.zeroLineWidth) let pos = transformer.pixelForValues(x: 0.0, y: 0.0) if yAxis.zeroLineDashLengths != nil { context.setLineDash(phase: yAxis.zeroLineDashPhase, lengths: yAxis.zeroLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: pos.y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: pos.y)) context.drawPath(using: CGPathDrawingMode.stroke) } open override func renderLimitLines(context: CGContext) { guard let yAxis = self.axis as? YAxis, let transformer = self.transformer else { return } var limitLines = yAxis.limitLines if limitLines.count == 0 { return } context.saveGState() let trans = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) for i in 0 ..< limitLines.count { let l = limitLines[i] if !l.isEnabled { continue } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.y -= l.lineWidth / 2.0 clippingRect.size.height += l.lineWidth context.clip(to: clippingRect) position.x = 0.0 position.y = CGFloat(l.limit) position = position.applying(trans) context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: position.y)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: position.y)) context.setStrokeColor(l.lineColor.cgColor) context.setLineWidth(l.lineWidth) if l.lineDashLengths != nil { context.setLineDash(phase: l.lineDashPhase, lengths: l.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.strokePath() let label = l.label // if drawing the limit-value label is enabled if l.drawLabelEnabled && label.count > 0 { let labelLineHeight = l.valueFont.lineHeight let xOffset: CGFloat = 4.0 + l.xOffset let yOffset: CGFloat = l.lineWidth + labelLineHeight + l.yOffset if l.labelPosition == .rightTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentRight - xOffset, y: position.y - yOffset), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .rightBottom { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentRight - xOffset, y: position.y + yOffset - labelLineHeight), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .leftTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentLeft + xOffset, y: position.y - yOffset), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: viewPortHandler.contentLeft + xOffset, y: position.y + yOffset - labelLineHeight), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } } } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/YAxisRendererHorizontalBarChart.swift ================================================ // // YAxisRendererHorizontalBarChart.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class YAxisRendererHorizontalBarChart: YAxisRenderer { public override init(viewPortHandler: ViewPortHandler, yAxis: YAxis?, transformer: Transformer?) { super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: transformer) } /// Computes the axis values. open override func computeAxis(min: Double, max: Double, inverted: Bool) { guard let transformer = self.transformer else { return } var min = min, max = max // calculate the starting and entry point of the y-labels (depending on zoom / contentrect bounds) if viewPortHandler.contentHeight > 10.0 && !viewPortHandler.isFullyZoomedOutX { let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) if !inverted { min = Double(p1.x) max = Double(p2.x) } else { min = Double(p2.x) max = Double(p1.x) } } computeAxisValues(min: min, max: max) } /// draws the y-axis labels to the screen open override func renderAxisLabels(context: CGContext) { guard let yAxis = axis as? YAxis else { return } if !yAxis.isEnabled || !yAxis.isDrawLabelsEnabled { return } let lineHeight = yAxis.labelFont.lineHeight let baseYOffset: CGFloat = 2.5 let dependency = yAxis.axisDependency let labelPosition = yAxis.labelPosition var yPos: CGFloat = 0.0 if dependency == .left { if labelPosition == .outsideChart { yPos = viewPortHandler.contentTop - baseYOffset } else { yPos = viewPortHandler.contentTop - baseYOffset } } else { if labelPosition == .outsideChart { yPos = viewPortHandler.contentBottom + lineHeight + baseYOffset } else { yPos = viewPortHandler.contentBottom + lineHeight + baseYOffset } } // For compatibility with Android code, we keep above calculation the same, // And here we pull the line back up yPos -= lineHeight drawYLabels( context: context, fixedPosition: yPos, positions: transformedPositions(), offset: yAxis.yOffset) } open override func renderAxisLine(context: CGContext) { guard let yAxis = axis as? YAxis else { return } if !yAxis.isEnabled || !yAxis.drawAxisLineEnabled { return } context.saveGState() context.setStrokeColor(yAxis.axisLineColor.cgColor) context.setLineWidth(yAxis.axisLineWidth) if yAxis.axisLineDashLengths != nil { context.setLineDash(phase: yAxis.axisLineDashPhase, lengths: yAxis.axisLineDashLengths) } else { context.setLineDash(phase: 0.0, lengths: []) } if yAxis.axisDependency == .left { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) context.strokePath() } else { context.beginPath() context.move(to: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) context.addLine(to: CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentBottom)) context.strokePath() } context.restoreGState() } /// draws the y-labels on the specified x-position @objc open func drawYLabels( context: CGContext, fixedPosition: CGFloat, positions: [CGPoint], offset: CGFloat) { guard let yAxis = axis as? YAxis else { return } let labelFont = yAxis.labelFont let labelTextColor = yAxis.labelTextColor let from = yAxis.isDrawBottomYLabelEntryEnabled ? 0 : 1 let to = yAxis.isDrawTopYLabelEntryEnabled ? yAxis.entryCount : (yAxis.entryCount - 1) for i in stride(from: from, to: to, by: 1) { let text = yAxis.getFormattedLabel(i) ChartUtils.drawText( context: context, text: text, point: CGPoint(x: positions[i].x, y: fixedPosition - offset), align: .center, attributes: [NSAttributedString.Key.font: labelFont, NSAttributedString.Key.foregroundColor: labelTextColor]) } } open override var gridClippingRect: CGRect { var contentRect = viewPortHandler.contentRect let dx = self.axis?.gridLineWidth ?? 0.0 contentRect.origin.x -= dx / 2.0 contentRect.size.width += dx return contentRect } open override func drawGridLine( context: CGContext, position: CGPoint) { context.beginPath() context.move(to: CGPoint(x: position.x, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: position.x, y: viewPortHandler.contentBottom)) context.strokePath() } open override func transformedPositions() -> [CGPoint] { guard let yAxis = self.axis as? YAxis, let transformer = self.transformer else { return [CGPoint]() } var positions = [CGPoint]() positions.reserveCapacity(yAxis.entryCount) let entries = yAxis.entries for i in stride(from: 0, to: yAxis.entryCount, by: 1) { positions.append(CGPoint(x: entries[i], y: 0.0)) } transformer.pointValuesToPixel(&positions) return positions } /// Draws the zero line at the specified position. open override func drawZeroLine(context: CGContext) { guard let yAxis = self.axis as? YAxis, let transformer = self.transformer, let zeroLineColor = yAxis.zeroLineColor else { return } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.x -= yAxis.zeroLineWidth / 2.0 clippingRect.size.width += yAxis.zeroLineWidth context.clip(to: clippingRect) context.setStrokeColor(zeroLineColor.cgColor) context.setLineWidth(yAxis.zeroLineWidth) let pos = transformer.pixelForValues(x: 0.0, y: 0.0) if yAxis.zeroLineDashLengths != nil { context.setLineDash(phase: yAxis.zeroLineDashPhase, lengths: yAxis.zeroLineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.move(to: CGPoint(x: pos.x - 1.0, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: pos.x - 1.0, y: viewPortHandler.contentBottom)) context.drawPath(using: CGPathDrawingMode.stroke) } private var _limitLineSegmentsBuffer = [CGPoint](repeating: CGPoint(), count: 2) open override func renderLimitLines(context: CGContext) { guard let yAxis = axis as? YAxis, let transformer = self.transformer else { return } var limitLines = yAxis.limitLines if limitLines.count <= 0 { return } context.saveGState() let trans = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) for i in 0 ..< limitLines.count { let l = limitLines[i] if !l.isEnabled { continue } context.saveGState() defer { context.restoreGState() } var clippingRect = viewPortHandler.contentRect clippingRect.origin.x -= l.lineWidth / 2.0 clippingRect.size.width += l.lineWidth context.clip(to: clippingRect) position.x = CGFloat(l.limit) position.y = 0.0 position = position.applying(trans) context.beginPath() context.move(to: CGPoint(x: position.x, y: viewPortHandler.contentTop)) context.addLine(to: CGPoint(x: position.x, y: viewPortHandler.contentBottom)) context.setStrokeColor(l.lineColor.cgColor) context.setLineWidth(l.lineWidth) if l.lineDashLengths != nil { context.setLineDash(phase: l.lineDashPhase, lengths: l.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } context.strokePath() let label = l.label // if drawing the limit-value label is enabled if l.drawLabelEnabled && label.count > 0 { let labelLineHeight = l.valueFont.lineHeight let xOffset: CGFloat = l.lineWidth + l.xOffset let yOffset: CGFloat = 2.0 + l.yOffset if l.labelPosition == .rightTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x + xOffset, y: viewPortHandler.contentTop + yOffset), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .rightBottom { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x + xOffset, y: viewPortHandler.contentBottom - labelLineHeight - yOffset), align: .left, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else if l.labelPosition == .leftTop { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x - xOffset, y: viewPortHandler.contentTop + yOffset), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } else { ChartUtils.drawText(context: context, text: label, point: CGPoint( x: position.x - xOffset, y: viewPortHandler.contentBottom - labelLineHeight - yOffset), align: .right, attributes: [NSAttributedString.Key.font: l.valueFont, NSAttributedString.Key.foregroundColor: l.valueTextColor]) } } } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Renderers/YAxisRendererRadarChart.swift ================================================ // // YAxisRendererRadarChart.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class YAxisRendererRadarChart: YAxisRenderer { private weak var chart: RadarChartView? @objc public init(viewPortHandler: ViewPortHandler, yAxis: YAxis?, chart: RadarChartView) { super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: nil) self.chart = chart } open override func computeAxisValues(min yMin: Double, max yMax: Double) { guard let axis = axis as? YAxis else { return } let labelCount = axis.labelCount let range = abs(yMax - yMin) if labelCount == 0 || range <= 0 || range.isInfinite { axis.entries = [Double]() axis.centeredEntries = [Double]() return } // Find out how much spacing (in yValue space) between axis values let rawInterval = range / Double(labelCount) var interval = rawInterval.roundedToNextSignficant() // If granularity is enabled, then do not allow the interval to go below specified granularity. // This is used to avoid repeated values when rounding values for display. if axis.isGranularityEnabled { interval = interval < axis.granularity ? axis.granularity : interval } // Normalize interval let intervalMagnitude = pow(10.0, floor(log10(interval))).roundedToNextSignficant() let intervalSigDigit = Int(interval / intervalMagnitude) if intervalSigDigit > 5 { // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 // if it's 0.0 after floor(), we use the old value interval = floor(10.0 * intervalMagnitude) == 0.0 ? interval : floor(10.0 * intervalMagnitude) } let centeringEnabled = axis.isCenterAxisLabelsEnabled var n = centeringEnabled ? 1 : 0 // force label count if axis.isForceLabelsEnabled { let step = Double(range) / Double(labelCount - 1) // Ensure stops contains at least n elements. axis.entries.removeAll(keepingCapacity: true) axis.entries.reserveCapacity(labelCount) var v = yMin for _ in 0 ..< labelCount { axis.entries.append(v) v += step } n = labelCount } else { // no forced count var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval if centeringEnabled { first -= interval } let last = interval == 0.0 ? 0.0 : (floor(yMax / interval) * interval).nextUp if interval != 0.0 { for _ in stride(from: first, through: last, by: interval) { n += 1 } } n += 1 // Ensure stops contains at least n elements. axis.entries.removeAll(keepingCapacity: true) axis.entries.reserveCapacity(labelCount) var f = first var i = 0 while i < n { if f == 0.0 { // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) f = 0.0 } axis.entries.append(Double(f)) f += interval i += 1 } } // set decimals if interval < 1 { axis.decimals = Int(ceil(-log10(interval))) } else { axis.decimals = 0 } if centeringEnabled { axis.centeredEntries.reserveCapacity(n) axis.centeredEntries.removeAll() let offset = (axis.entries[1] - axis.entries[0]) / 2.0 for i in 0 ..< n { axis.centeredEntries.append(axis.entries[i] + offset) } } axis._axisMinimum = axis.entries[0] axis._axisMaximum = axis.entries[n-1] axis.axisRange = abs(axis._axisMaximum - axis._axisMinimum) } open override func renderAxisLabels(context: CGContext) { guard let yAxis = axis as? YAxis, let chart = chart else { return } if !yAxis.isEnabled || !yAxis.isDrawLabelsEnabled { return } let labelFont = yAxis.labelFont let labelTextColor = yAxis.labelTextColor let center = chart.centerOffsets let factor = chart.factor let labelLineHeight = yAxis.labelFont.lineHeight let from = yAxis.isDrawBottomYLabelEntryEnabled ? 0 : 1 let to = yAxis.isDrawTopYLabelEntryEnabled ? yAxis.entryCount : (yAxis.entryCount - 1) for j in stride(from: from, to: to, by: 1) { let r = CGFloat(yAxis.entries[j] - yAxis._axisMinimum) * factor let p = center.moving(distance: r, atAngle: chart.rotationAngle) let label = yAxis.getFormattedLabel(j) ChartUtils.drawText( context: context, text: label, point: CGPoint(x: p.x + 10.0, y: p.y - labelLineHeight), align: .left, attributes: [ NSAttributedString.Key.font: labelFont, NSAttributedString.Key.foregroundColor: labelTextColor ]) } } open override func renderLimitLines(context: CGContext) { guard let yAxis = axis as? YAxis, let chart = chart, let data = chart.data else { return } var limitLines = yAxis.limitLines if limitLines.count == 0 { return } context.saveGState() let sliceangle = chart.sliceAngle // calculate the factor that is needed for transforming the value to pixels let factor = chart.factor let center = chart.centerOffsets for i in 0 ..< limitLines.count { let l = limitLines[i] if !l.isEnabled { continue } context.setStrokeColor(l.lineColor.cgColor) context.setLineWidth(l.lineWidth) if l.lineDashLengths != nil { context.setLineDash(phase: l.lineDashPhase, lengths: l.lineDashLengths!) } else { context.setLineDash(phase: 0.0, lengths: []) } let r = CGFloat(l.limit - chart.chartYMin) * factor context.beginPath() for j in 0 ..< (data.maxEntryCountSet?.entryCount ?? 0) { let p = center.moving(distance: r, atAngle: sliceangle * CGFloat(j) + chart.rotationAngle) if j == 0 { context.move(to: CGPoint(x: p.x, y: p.y)) } else { context.addLine(to: CGPoint(x: p.x, y: p.y)) } } context.closePath() context.strokePath() } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/ChartColorTemplates.swift ================================================ // // ChartColorTemplates.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics #if !os(OSX) import UIKit #endif open class ChartColorTemplates: NSObject { @objc open class func liberty () -> [NSUIColor] { return [ NSUIColor(red: 207/255.0, green: 248/255.0, blue: 246/255.0, alpha: 1.0), NSUIColor(red: 148/255.0, green: 212/255.0, blue: 212/255.0, alpha: 1.0), NSUIColor(red: 136/255.0, green: 180/255.0, blue: 187/255.0, alpha: 1.0), NSUIColor(red: 118/255.0, green: 174/255.0, blue: 175/255.0, alpha: 1.0), NSUIColor(red: 42/255.0, green: 109/255.0, blue: 130/255.0, alpha: 1.0) ] } @objc open class func joyful () -> [NSUIColor] { return [ NSUIColor(red: 217/255.0, green: 80/255.0, blue: 138/255.0, alpha: 1.0), NSUIColor(red: 254/255.0, green: 149/255.0, blue: 7/255.0, alpha: 1.0), NSUIColor(red: 254/255.0, green: 247/255.0, blue: 120/255.0, alpha: 1.0), NSUIColor(red: 106/255.0, green: 167/255.0, blue: 134/255.0, alpha: 1.0), NSUIColor(red: 53/255.0, green: 194/255.0, blue: 209/255.0, alpha: 1.0) ] } @objc open class func pastel () -> [NSUIColor] { return [ NSUIColor(red: 64/255.0, green: 89/255.0, blue: 128/255.0, alpha: 1.0), NSUIColor(red: 149/255.0, green: 165/255.0, blue: 124/255.0, alpha: 1.0), NSUIColor(red: 217/255.0, green: 184/255.0, blue: 162/255.0, alpha: 1.0), NSUIColor(red: 191/255.0, green: 134/255.0, blue: 134/255.0, alpha: 1.0), NSUIColor(red: 179/255.0, green: 48/255.0, blue: 80/255.0, alpha: 1.0) ] } @objc open class func colorful () -> [NSUIColor] { return [ NSUIColor(red: 193/255.0, green: 37/255.0, blue: 82/255.0, alpha: 1.0), NSUIColor(red: 255/255.0, green: 102/255.0, blue: 0/255.0, alpha: 1.0), NSUIColor(red: 245/255.0, green: 199/255.0, blue: 0/255.0, alpha: 1.0), NSUIColor(red: 106/255.0, green: 150/255.0, blue: 31/255.0, alpha: 1.0), NSUIColor(red: 179/255.0, green: 100/255.0, blue: 53/255.0, alpha: 1.0) ] } @objc open class func vordiplom () -> [NSUIColor] { return [ NSUIColor(red: 192/255.0, green: 255/255.0, blue: 140/255.0, alpha: 1.0), NSUIColor(red: 255/255.0, green: 247/255.0, blue: 140/255.0, alpha: 1.0), NSUIColor(red: 255/255.0, green: 208/255.0, blue: 140/255.0, alpha: 1.0), NSUIColor(red: 140/255.0, green: 234/255.0, blue: 255/255.0, alpha: 1.0), NSUIColor(red: 255/255.0, green: 140/255.0, blue: 157/255.0, alpha: 1.0) ] } @objc open class func material () -> [NSUIColor] { return [ NSUIColor(red: 46/255.0, green: 204/255.0, blue: 113/255.0, alpha: 1.0), NSUIColor(red: 241/255.0, green: 196/255.0, blue: 15/255.0, alpha: 1.0), NSUIColor(red: 231/255.0, green: 76/255.0, blue: 60/255.0, alpha: 1.0), NSUIColor(red: 52/255.0, green: 152/255.0, blue: 219/255.0, alpha: 1.0) ] } @objc open class func colorFromString(_ colorString: String) -> NSUIColor { let leftParenCharset: CharacterSet = CharacterSet(charactersIn: "( ") let commaCharset: CharacterSet = CharacterSet(charactersIn: ", ") let colorString = colorString.lowercased() if colorString.hasPrefix("#") { var argb: [UInt] = [255, 0, 0, 0] let colorString = colorString.unicodeScalars var length = colorString.count var index = colorString.startIndex let endIndex = colorString.endIndex index = colorString.index(after: index) length = length - 1 if length == 3 || length == 6 || length == 8 { var i = length == 8 ? 0 : 1 while index < endIndex { var c = colorString[index] index = colorString.index(after: index) var val = (c.value >= 0x61 && c.value <= 0x66) ? (c.value - 0x61 + 10) : c.value - 0x30 argb[i] = UInt(val) * 16 if length == 3 { argb[i] = argb[i] + UInt(val) } else { c = colorString[index] index = colorString.index(after: index) val = (c.value >= 0x61 && c.value <= 0x66) ? (c.value - 0x61 + 10) : c.value - 0x30 argb[i] = argb[i] + UInt(val) } i += 1 } } return NSUIColor(red: CGFloat(argb[1]) / 255.0, green: CGFloat(argb[2]) / 255.0, blue: CGFloat(argb[3]) / 255.0, alpha: CGFloat(argb[0]) / 255.0) } else if colorString.hasPrefix("rgba") { var a: Float = 1.0 var r: Int32 = 0 var g: Int32 = 0 var b: Int32 = 0 let scanner: Scanner = Scanner(string: colorString) scanner.scanString("rgba", into: nil) scanner.scanCharacters(from: leftParenCharset, into: nil) scanner.scanInt32(&r) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&g) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&b) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanFloat(&a) return NSUIColor( red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(a) ) } else if colorString.hasPrefix("argb") { var a: Float = 1.0 var r: Int32 = 0 var g: Int32 = 0 var b: Int32 = 0 let scanner: Scanner = Scanner(string: colorString) scanner.scanString("argb", into: nil) scanner.scanCharacters(from: leftParenCharset, into: nil) scanner.scanFloat(&a) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&r) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&g) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&b) return NSUIColor( red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(a) ) } else if colorString.hasPrefix("rgb") { var r: Int32 = 0 var g: Int32 = 0 var b: Int32 = 0 let scanner: Scanner = Scanner(string: colorString) scanner.scanString("rgb", into: nil) scanner.scanCharacters(from: leftParenCharset, into: nil) scanner.scanInt32(&r) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&g) scanner.scanCharacters(from: commaCharset, into: nil) scanner.scanInt32(&b) return NSUIColor( red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: 1.0 ) } return NSUIColor.clear } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/ChartUtils.swift ================================================ // // Utils.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics extension FloatingPoint { var DEG2RAD: Self { return self * .pi / 180 } var RAD2DEG: Self { return self * 180 / .pi } /// - returns: An angle between 0.0 < 360.0 (not less than zero, less than 360) /// NOTE: Value must be in degrees var normalizedAngle: Self { let angle = truncatingRemainder(dividingBy: 360) return (sign == .minus) ? angle + 360 : angle } } extension CGSize { func rotatedBy(degrees: CGFloat) -> CGSize { let radians = degrees.DEG2RAD return rotatedBy(radians: radians) } func rotatedBy(radians: CGFloat) -> CGSize { return CGSize( width: abs(width * cos(radians)) + abs(height * sin(radians)), height: abs(width * sin(radians)) + abs(height * cos(radians)) ) } } extension Double { /// Rounds the number to the nearest multiple of it's order of magnitude, rounding away from zero if halfway. func roundedToNextSignficant() -> Double { guard !isInfinite, !isNaN, self != 0 else { return self } let d = ceil(log10(self < 0 ? -self : self)) let pw = 1 - Int(d) let magnitude = pow(10.0, Double(pw)) let shifted = (self * magnitude).rounded() return shifted / magnitude } var decimalPlaces: Int { guard !isNaN, !isInfinite, self != 0.0 else { return 0 } let i = self.roundedToNextSignficant() guard !i.isInfinite, !i.isNaN else { return 0 } return Int(ceil(-log10(i))) + 2 } } extension CGPoint { /// Calculates the position around a center point, depending on the distance from the center, and the angle of the position around the center. func moving(distance: CGFloat, atAngle angle: CGFloat) -> CGPoint { return CGPoint(x: x + distance * cos(angle.DEG2RAD), y: y + distance * sin(angle.DEG2RAD)) } } open class ChartUtils { private static var _defaultValueFormatter: IValueFormatter = ChartUtils.generateDefaultValueFormatter() open class func drawImage( context: CGContext, image: NSUIImage, x: CGFloat, y: CGFloat, size: CGSize) { var drawOffset = CGPoint() drawOffset.x = x - (size.width / 2) drawOffset.y = y - (size.height / 2) NSUIGraphicsPushContext(context) if image.size.width != size.width && image.size.height != size.height { let key = "resized_\(size.width)_\(size.height)" // Try to take scaled image from cache of this image var scaledImage = objc_getAssociatedObject(image, key) as? NSUIImage if scaledImage == nil { // Scale the image NSUIGraphicsBeginImageContextWithOptions(size, false, 0.0) image.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: size)) scaledImage = NSUIGraphicsGetImageFromCurrentImageContext() NSUIGraphicsEndImageContext() // Put the scaled image in a cache owned by the original image objc_setAssociatedObject(image, key, scaledImage, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } scaledImage?.draw(in: CGRect(origin: drawOffset, size: size)) } else { image.draw(in: CGRect(origin: drawOffset, size: size)) } NSUIGraphicsPopContext() } open class func drawText(context: CGContext, text: String, point: CGPoint, align: NSTextAlignment, attributes: [NSAttributedString.Key : Any]?) { var point = point if align == .center { point.x -= text.size(withAttributes: attributes).width / 2.0 } else if align == .right { point.x -= text.size(withAttributes: attributes).width } NSUIGraphicsPushContext(context) (text as NSString).draw(at: point, withAttributes: attributes) NSUIGraphicsPopContext() } open class func drawText(context: CGContext, text: String, point: CGPoint, attributes: [NSAttributedString.Key : Any]?, anchor: CGPoint, angleRadians: CGFloat) { var drawOffset = CGPoint() NSUIGraphicsPushContext(context) if angleRadians != 0.0 { let size = text.size(withAttributes: attributes) // Move the text drawing rect in a way that it always rotates around its center drawOffset.x = -size.width * 0.5 drawOffset.y = -size.height * 0.5 var translate = point // Move the "outer" rect relative to the anchor, assuming its centered if anchor.x != 0.5 || anchor.y != 0.5 { let rotatedSize = size.rotatedBy(radians: angleRadians) translate.x -= rotatedSize.width * (anchor.x - 0.5) translate.y -= rotatedSize.height * (anchor.y - 0.5) } context.saveGState() context.translateBy(x: translate.x, y: translate.y) context.rotate(by: angleRadians) (text as NSString).draw(at: drawOffset, withAttributes: attributes) context.restoreGState() } else { if anchor.x != 0.0 || anchor.y != 0.0 { let size = text.size(withAttributes: attributes) drawOffset.x = -size.width * anchor.x drawOffset.y = -size.height * anchor.y } drawOffset.x += point.x drawOffset.y += point.y (text as NSString).draw(at: drawOffset, withAttributes: attributes) } NSUIGraphicsPopContext() } internal class func drawMultilineText(context: CGContext, text: String, knownTextSize: CGSize, point: CGPoint, attributes: [NSAttributedString.Key : Any]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) { var rect = CGRect(origin: CGPoint(), size: knownTextSize) NSUIGraphicsPushContext(context) if angleRadians != 0.0 { // Move the text drawing rect in a way that it always rotates around its center rect.origin.x = -knownTextSize.width * 0.5 rect.origin.y = -knownTextSize.height * 0.5 var translate = point // Move the "outer" rect relative to the anchor, assuming its centered if anchor.x != 0.5 || anchor.y != 0.5 { let rotatedSize = knownTextSize.rotatedBy(radians: angleRadians) translate.x -= rotatedSize.width * (anchor.x - 0.5) translate.y -= rotatedSize.height * (anchor.y - 0.5) } context.saveGState() context.translateBy(x: translate.x, y: translate.y) context.rotate(by: angleRadians) (text as NSString).draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) context.restoreGState() } else { if anchor.x != 0.0 || anchor.y != 0.0 { rect.origin.x = -knownTextSize.width * anchor.x rect.origin.y = -knownTextSize.height * anchor.y } rect.origin.x += point.x rect.origin.y += point.y (text as NSString).draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) } NSUIGraphicsPopContext() } internal class func drawMultilineText(context: CGContext, text: String, point: CGPoint, attributes: [NSAttributedString.Key : Any]?, constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) { let rect = text.boundingRect(with: constrainedToSize, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) drawMultilineText(context: context, text: text, knownTextSize: rect.size, point: point, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) } private class func generateDefaultValueFormatter() -> IValueFormatter { let formatter = DefaultValueFormatter(decimals: 1) return formatter } /// - returns: The default value formatter used for all chart components that needs a default open class func defaultValueFormatter() -> IValueFormatter { return _defaultValueFormatter } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/Fill.swift ================================================ // // Fill.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartFillType) public enum FillType: Int { case empty case color case linearGradient case radialGradient case image case tiledImage case layer } @objc(ChartFill) open class Fill: NSObject { private var _type: FillType = FillType.empty private var _color: CGColor? private var _gradient: CGGradient? private var _gradientAngle: CGFloat = 0.0 private var _gradientStartOffsetPercent: CGPoint = CGPoint() private var _gradientStartRadiusPercent: CGFloat = 0.0 private var _gradientEndOffsetPercent: CGPoint = CGPoint() private var _gradientEndRadiusPercent: CGFloat = 0.0 private var _image: CGImage? private var _layer: CGLayer? // MARK: Properties @objc open var type: FillType { return _type } @objc open var color: CGColor? { return _color } @objc open var gradient: CGGradient? { return _gradient } @objc open var gradientAngle: CGFloat { return _gradientAngle } @objc open var gradientStartOffsetPercent: CGPoint { return _gradientStartOffsetPercent } @objc open var gradientStartRadiusPercent: CGFloat { return _gradientStartRadiusPercent } @objc open var gradientEndOffsetPercent: CGPoint { return _gradientEndOffsetPercent } @objc open var gradientEndRadiusPercent: CGFloat { return _gradientEndRadiusPercent } @objc open var image: CGImage? { return _image } @objc open var layer: CGLayer? { return _layer } // MARK: Constructors public override init() { } @objc public init(CGColor: CGColor) { _type = .color _color = CGColor } @objc public convenience init(color: NSUIColor) { self.init(CGColor: color.cgColor) } @objc public init(linearGradient: CGGradient, angle: CGFloat) { _type = .linearGradient _gradient = linearGradient _gradientAngle = angle } @objc public init( radialGradient: CGGradient, startOffsetPercent: CGPoint, startRadiusPercent: CGFloat, endOffsetPercent: CGPoint, endRadiusPercent: CGFloat ) { _type = .radialGradient _gradient = radialGradient _gradientStartOffsetPercent = startOffsetPercent _gradientStartRadiusPercent = startRadiusPercent _gradientEndOffsetPercent = endOffsetPercent _gradientEndRadiusPercent = endRadiusPercent } @objc public convenience init(radialGradient: CGGradient) { self.init( radialGradient: radialGradient, startOffsetPercent: CGPoint(x: 0.0, y: 0.0), startRadiusPercent: 0.0, endOffsetPercent: CGPoint(x: 0.0, y: 0.0), endRadiusPercent: 1.0 ) } @objc public init(CGImage: CGImage, tiled: Bool) { _type = tiled ? .tiledImage : .image _image = CGImage } @objc public convenience init(image: NSUIImage, tiled: Bool) { self.init(CGImage: image.cgImage!, tiled: tiled) } @objc public convenience init(CGImage: CGImage) { self.init(CGImage: CGImage, tiled: false) } @objc public convenience init(image: NSUIImage) { self.init(image: image, tiled: false) } @objc public init(CGLayer: CGLayer) { _type = .layer _layer = CGLayer } // MARK: Constructors @objc open class func fillWithCGColor(_ CGColor: CGColor) -> Fill { return Fill(CGColor: CGColor) } @objc open class func fillWithColor(_ color: NSUIColor) -> Fill { return Fill(color: color) } @objc open class func fillWithLinearGradient( _ linearGradient: CGGradient, angle: CGFloat) -> Fill { return Fill(linearGradient: linearGradient, angle: angle) } @objc open class func fillWithRadialGradient( _ radialGradient: CGGradient, startOffsetPercent: CGPoint, startRadiusPercent: CGFloat, endOffsetPercent: CGPoint, endRadiusPercent: CGFloat ) -> Fill { return Fill( radialGradient: radialGradient, startOffsetPercent: startOffsetPercent, startRadiusPercent: startRadiusPercent, endOffsetPercent: endOffsetPercent, endRadiusPercent: endRadiusPercent ) } @objc open class func fillWithRadialGradient(_ radialGradient: CGGradient) -> Fill { return Fill(radialGradient: radialGradient) } @objc open class func fillWithCGImage(_ CGImage: CGImage, tiled: Bool) -> Fill { return Fill(CGImage: CGImage, tiled: tiled) } @objc open class func fillWithImage(_ image: NSUIImage, tiled: Bool) -> Fill { return Fill(image: image, tiled: tiled) } @objc open class func fillWithCGImage(_ CGImage: CGImage) -> Fill { return Fill(CGImage: CGImage) } @objc open class func fillWithImage(_ image: NSUIImage) -> Fill { return Fill(image: image) } @objc open class func fillWithCGLayer(_ CGLayer: CGLayer) -> Fill { return Fill(CGLayer: CGLayer) } // MARK: Drawing code /// Draws the provided path in filled mode with the provided area @objc open func fillPath( context: CGContext, rect: CGRect) { let fillType = _type if fillType == .empty { return } context.saveGState() switch fillType { case .color: context.setFillColor(_color!) context.fillPath() case .image: context.clip() context.draw(_image!, in: rect) case .tiledImage: context.clip() context.draw(_image!, in: rect, byTiling: true) case .layer: context.clip() context.draw(_layer!, in: rect) case .linearGradient: let radians = (360.0 - _gradientAngle).DEG2RAD let centerPoint = CGPoint(x: rect.midX, y: rect.midY) let xAngleDelta = cos(radians) * rect.width / 2.0 let yAngleDelta = sin(radians) * rect.height / 2.0 let startPoint = CGPoint( x: centerPoint.x - xAngleDelta, y: centerPoint.y - yAngleDelta ) let endPoint = CGPoint( x: centerPoint.x + xAngleDelta, y: centerPoint.y + yAngleDelta ) context.clip() context.drawLinearGradient(_gradient!, start: startPoint, end: endPoint, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation] ) case .radialGradient: let centerPoint = CGPoint(x: rect.midX, y: rect.midY) let radius = max(rect.width, rect.height) / 2.0 context.clip() context.drawRadialGradient(_gradient!, startCenter: CGPoint( x: centerPoint.x + rect.width * _gradientStartOffsetPercent.x, y: centerPoint.y + rect.height * _gradientStartOffsetPercent.y ), startRadius: radius * _gradientStartRadiusPercent, endCenter: CGPoint( x: centerPoint.x + rect.width * _gradientEndOffsetPercent.x, y: centerPoint.y + rect.height * _gradientEndOffsetPercent.y ), endRadius: radius * _gradientEndRadiusPercent, options: [.drawsAfterEndLocation, .drawsBeforeStartLocation] ) case .empty: break } context.restoreGState() } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/Platform.swift ================================================ import Foundation /** This file provides a thin abstraction layer atop of UIKit (iOS, tvOS) and Cocoa (OS X). The two APIs are very much alike, and for the chart library's usage of the APIs it is often sufficient to typealias one to the other. The NSUI* types are aliased to either their UI* implementation (on iOS) or their NS* implementation (on OS X). */ #if os(iOS) || os(tvOS) import UIKit public typealias NSUIFont = UIFont public typealias NSUIColor = UIColor public typealias NSUIEvent = UIEvent public typealias NSUITouch = UITouch public typealias NSUIImage = UIImage public typealias NSUIScrollView = UIScrollView public typealias NSUIGestureRecognizer = UIGestureRecognizer public typealias NSUIGestureRecognizerState = UIGestureRecognizer.State public typealias NSUIGestureRecognizerDelegate = UIGestureRecognizerDelegate public typealias NSUITapGestureRecognizer = UITapGestureRecognizer public typealias NSUIPanGestureRecognizer = UIPanGestureRecognizer #if !os(tvOS) public typealias NSUIPinchGestureRecognizer = UIPinchGestureRecognizer public typealias NSUIRotationGestureRecognizer = UIRotationGestureRecognizer #endif public typealias NSUIScreen = UIScreen public typealias NSUIDisplayLink = CADisplayLink extension NSUITapGestureRecognizer { @objc final func nsuiNumberOfTouches() -> Int { return numberOfTouches } @objc final var nsuiNumberOfTapsRequired: Int { get { return self.numberOfTapsRequired } set { self.numberOfTapsRequired = newValue } } } extension NSUIPanGestureRecognizer { @objc final func nsuiNumberOfTouches() -> Int { return numberOfTouches } @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint { return super.location(ofTouch: touch, in: inView) } } #if !os(tvOS) extension NSUIRotationGestureRecognizer { @objc final var nsuiRotation: CGFloat { get { return rotation } set { rotation = newValue } } } #endif #if !os(tvOS) extension NSUIPinchGestureRecognizer { @objc final var nsuiScale: CGFloat { get { return scale } set { scale = newValue } } @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint { return super.location(ofTouch: touch, in: inView) } } #endif open class NSUIView: UIView { public final override func touchesBegan(_ touches: Set, with event: NSUIEvent?) { self.nsuiTouchesBegan(touches, withEvent: event) } public final override func touchesMoved(_ touches: Set, with event: NSUIEvent?) { self.nsuiTouchesMoved(touches, withEvent: event) } public final override func touchesEnded(_ touches: Set, with event: NSUIEvent?) { self.nsuiTouchesEnded(touches, withEvent: event) } public final override func touchesCancelled(_ touches: Set, with event: NSUIEvent?) { self.nsuiTouchesCancelled(touches, withEvent: event) } @objc open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesBegan(touches, with: event!) } @objc open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesMoved(touches, with: event!) } @objc open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesEnded(touches, with: event!) } @objc open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) { super.touchesCancelled(touches!, with: event!) } @objc var nsuiLayer: CALayer? { return self.layer } } extension UIView { @objc final var nsuiGestureRecognizers: [NSUIGestureRecognizer]? { return self.gestureRecognizers } } extension UIScrollView { @objc var nsuiIsScrollEnabled: Bool { get { return isScrollEnabled } set { isScrollEnabled = newValue } } } extension UIScreen { @objc final var nsuiScale: CGFloat { return self.scale } } func NSUIGraphicsGetCurrentContext() -> CGContext? { return UIGraphicsGetCurrentContext() } func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage! { return UIGraphicsGetImageFromCurrentImageContext() } func NSUIGraphicsPushContext(_ context: CGContext) { UIGraphicsPushContext(context) } func NSUIGraphicsPopContext() { UIGraphicsPopContext() } func NSUIGraphicsEndImageContext() { UIGraphicsEndImageContext() } func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? { return NSUIImagePNGRepresentation(image) } func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.8) -> Data? { return NSUIImageJPEGRepresentation(image, quality) } func NSUIMainScreen() -> NSUIScreen? { return NSUIScreen.main } func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) { UIGraphicsBeginImageContextWithOptions(size, opaque, scale) } #endif #if os(OSX) import Cocoa import Quartz public typealias NSUIFont = NSFont public typealias NSUIColor = NSColor public typealias NSUIEvent = NSEvent public typealias NSUITouch = NSTouch public typealias NSUIImage = NSImage public typealias NSUIScrollView = NSScrollView public typealias NSUIGestureRecognizer = NSGestureRecognizer public typealias NSUIGestureRecognizerState = NSGestureRecognizer.State public typealias NSUIGestureRecognizerDelegate = NSGestureRecognizerDelegate public typealias NSUITapGestureRecognizer = NSClickGestureRecognizer public typealias NSUIPanGestureRecognizer = NSPanGestureRecognizer public typealias NSUIPinchGestureRecognizer = NSMagnificationGestureRecognizer public typealias NSUIRotationGestureRecognizer = NSRotationGestureRecognizer public typealias NSUIScreen = NSScreen /** On OS X there is no CADisplayLink. Use a 60 fps timer to render the animations. */ public class NSUIDisplayLink { private var timer: Timer? private var displayLink: CVDisplayLink? private var _timestamp: CFTimeInterval = 0.0 private weak var _target: AnyObject? private var _selector: Selector public var timestamp: CFTimeInterval { return _timestamp } init(target: AnyObject, selector: Selector) { _target = target _selector = selector if CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) == kCVReturnSuccess { CVDisplayLinkSetOutputCallback(displayLink!, { (displayLink, inNow, inOutputTime, flagsIn, flagsOut, userData) -> CVReturn in let _self = unsafeBitCast(userData, to: NSUIDisplayLink.self) _self._timestamp = CFAbsoluteTimeGetCurrent() _self._target?.performSelector(onMainThread: _self._selector, with: _self, waitUntilDone: false) return kCVReturnSuccess }, Unmanaged.passUnretained(self).toOpaque()) } else { timer = Timer(timeInterval: 1.0 / 60.0, target: target, selector: selector, userInfo: nil, repeats: true) } } deinit { stop() } open func add(to runloop: RunLoop, forMode mode: RunLoopMode) { if displayLink != nil { CVDisplayLinkStart(displayLink!) } else if timer != nil { runloop.add(timer!, forMode: mode) } } open func remove(from: RunLoop, forMode: RunLoopMode) { stop() } private func stop() { if displayLink != nil { CVDisplayLinkStop(displayLink!) } if timer != nil { timer?.invalidate() } } } /** The 'tap' gesture is mapped to clicks. */ extension NSUITapGestureRecognizer { final func nsuiNumberOfTouches() -> Int { return 1 } final var nsuiNumberOfTapsRequired: Int { get { return self.numberOfClicksRequired } set { self.numberOfClicksRequired = newValue } } } extension NSUIPanGestureRecognizer { final func nsuiNumberOfTouches() -> Int { return 1 } /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. final func nsuiLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint { return super.location(in: inView) } } extension NSUIRotationGestureRecognizer { /// FIXME: Currently there are no velocities in OSX gestures, and not way to create custom touch gestures. final var velocity: CGFloat { return 0.1 } final var nsuiRotation: CGFloat { get { return -rotation } set { rotation = -newValue } } } extension NSUIPinchGestureRecognizer { final var nsuiScale: CGFloat { get { return magnification + 1.0 } set { magnification = newValue - 1.0 } } /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. final func nsuiLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint { return super.location(in: view) } } extension NSView { final var nsuiGestureRecognizers: [NSGestureRecognizer]? { return self.gestureRecognizers } } extension NSScrollView { var nsuiIsScrollEnabled: Bool { get { return scrollEnabled } set { scrollEnabled = newValue } } } open class NSUIView: NSView { public final override var isFlipped: Bool { return true } func setNeedsDisplay() { self.setNeedsDisplay(self.bounds) } public final override func touchesBegan(with event: NSEvent) { self.nsuiTouchesBegan(event.touches(matching: .any, in: self), withEvent: event) } public final override func touchesEnded(with event: NSEvent) { self.nsuiTouchesEnded(event.touches(matching: .any, in: self), withEvent: event) } public final override func touchesMoved(with event: NSEvent) { self.nsuiTouchesMoved(event.touches(matching: .any, in: self), withEvent: event) } open override func touchesCancelled(with event: NSEvent) { self.nsuiTouchesCancelled(event.touches(matching: .any, in: self), withEvent: event) } open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesBegan(with: event!) } open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesMoved(with: event!) } open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) { super.touchesEnded(with: event!) } open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) { super.touchesCancelled(with: event!) } open var backgroundColor: NSUIColor? { get { return self.layer?.backgroundColor == nil ? nil : NSColor(cgColor: self.layer!.backgroundColor!) } set { self.wantsLayer = true self.layer?.backgroundColor = newValue == nil ? nil : newValue!.cgColor } } final var nsuiLayer: CALayer? { return self.layer } } extension NSFont { var lineHeight: CGFloat { // Not sure if this is right, but it looks okay return self.boundingRectForFont.size.height } } extension NSScreen { final var nsuiScale: CGFloat { return self.backingScaleFactor } } extension NSImage { var cgImage: CGImage? { return self.cgImage(forProposedRect: nil, context: nil, hints: nil) } } extension NSTouch { /** Touch locations on OS X are relative to the trackpad, whereas on iOS they are actually *on* the view. */ func locationInView(view: NSView) -> NSPoint { let n = self.normalizedPosition let b = view.bounds return NSPoint(x: b.origin.x + b.size.width * n.x, y: b.origin.y + b.size.height * n.y) } } extension NSScrollView { var scrollEnabled: Bool { get { return true } set { // FIXME: We can't disable scrolling it on OSX } } } func NSUIGraphicsGetCurrentContext() -> CGContext? { return NSGraphicsContext.current?.cgContext } func NSUIGraphicsPushContext(_ context: CGContext) { let cx = NSGraphicsContext(cgContext: context, flipped: true) NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = cx } func NSUIGraphicsPopContext() { NSGraphicsContext.restoreGraphicsState() } func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? { image.lockFocus() let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) image.unlockFocus() return rep?.representation(using: .png, properties: [:]) } func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.9) -> Data? { image.lockFocus() let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) image.unlockFocus() return rep?.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality]) } private var imageContextStack: [CGFloat] = [] func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) { var scale = scale if scale == 0.0 { scale = NSScreen.main?.backingScaleFactor ?? 1.0 } let width = Int(size.width * scale) let height = Int(size.height * scale) if width > 0 && height > 0 { imageContextStack.append(scale) let colorSpace = CGColorSpaceCreateDeviceRGB() guard let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4*width, space: colorSpace, bitmapInfo: (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue)) else { return } ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(height))) ctx.scaleBy(x: scale, y: scale) NSUIGraphicsPushContext(ctx) } } func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage? { if !imageContextStack.isEmpty { guard let ctx = NSUIGraphicsGetCurrentContext() else { return nil } let scale = imageContextStack.last! if let theCGImage = ctx.makeImage() { let size = CGSize(width: CGFloat(ctx.width) / scale, height: CGFloat(ctx.height) / scale) let image = NSImage(cgImage: theCGImage, size: size) return image } } return nil } func NSUIGraphicsEndImageContext() { if imageContextStack.last != nil { imageContextStack.removeLast() NSUIGraphicsPopContext() } } func NSUIMainScreen() -> NSUIScreen? { return NSUIScreen.main } #endif ================================================ FILE: Pods/Charts/Source/Charts/Utils/Transformer.swift ================================================ // // Transformer.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Transformer class that contains all matrices and is responsible for transforming values into pixels on the screen and backwards. @objc(ChartTransformer) open class Transformer: NSObject { /// matrix to map the values to the screen pixels internal var _matrixValueToPx = CGAffineTransform.identity /// matrix for handling the different offsets of the chart internal var _matrixOffset = CGAffineTransform.identity internal var _viewPortHandler: ViewPortHandler @objc public init(viewPortHandler: ViewPortHandler) { _viewPortHandler = viewPortHandler } /// Prepares the matrix that transforms values to pixels. Calculates the scale factors from the charts size and offsets. @objc open func prepareMatrixValuePx(chartXMin: Double, deltaX: CGFloat, deltaY: CGFloat, chartYMin: Double) { var scaleX = (_viewPortHandler.contentWidth / deltaX) var scaleY = (_viewPortHandler.contentHeight / deltaY) if CGFloat.infinity == scaleX { scaleX = 0.0 } if CGFloat.infinity == scaleY { scaleY = 0.0 } // setup all matrices _matrixValueToPx = CGAffineTransform.identity _matrixValueToPx = _matrixValueToPx.scaledBy(x: scaleX, y: -scaleY) _matrixValueToPx = _matrixValueToPx.translatedBy(x: CGFloat(-chartXMin), y: CGFloat(-chartYMin)) } /// Prepares the matrix that contains all offsets. @objc open func prepareMatrixOffset(inverted: Bool) { if !inverted { _matrixOffset = CGAffineTransform(translationX: _viewPortHandler.offsetLeft, y: _viewPortHandler.chartHeight - _viewPortHandler.offsetBottom) } else { _matrixOffset = CGAffineTransform(scaleX: 1.0, y: -1.0) _matrixOffset = _matrixOffset.translatedBy(x: _viewPortHandler.offsetLeft, y: -_viewPortHandler.offsetTop) } } /// Transform an array of points with all matrices. // VERY IMPORTANT: Keep matrix order "value-touch-offset" when transforming. open func pointValuesToPixel(_ points: inout [CGPoint]) { let trans = valueToPixelMatrix for i in 0 ..< points.count { points[i] = points[i].applying(trans) } } open func pointValueToPixel(_ point: inout CGPoint) { point = point.applying(valueToPixelMatrix) } @objc open func pixelForValues(x: Double, y: Double) -> CGPoint { return CGPoint(x: x, y: y).applying(valueToPixelMatrix) } /// Transform a rectangle with all matrices. open func rectValueToPixel(_ r: inout CGRect) { r = r.applying(valueToPixelMatrix) } /// Transform a rectangle with all matrices with potential animation phases. open func rectValueToPixel(_ r: inout CGRect, phaseY: Double) { // multiply the height of the rect with the phase var bottom = r.origin.y + r.size.height bottom *= CGFloat(phaseY) let top = r.origin.y * CGFloat(phaseY) r.size.height = bottom - top r.origin.y = top r = r.applying(valueToPixelMatrix) } /// Transform a rectangle with all matrices. open func rectValueToPixelHorizontal(_ r: inout CGRect) { r = r.applying(valueToPixelMatrix) } /// Transform a rectangle with all matrices with potential animation phases. open func rectValueToPixelHorizontal(_ r: inout CGRect, phaseY: Double) { // multiply the height of the rect with the phase let left = r.origin.x * CGFloat(phaseY) let right = (r.origin.x + r.size.width) * CGFloat(phaseY) r.size.width = right - left r.origin.x = left r = r.applying(valueToPixelMatrix) } /// transforms multiple rects with all matrices open func rectValuesToPixel(_ rects: inout [CGRect]) { let trans = valueToPixelMatrix for i in 0 ..< rects.count { rects[i] = rects[i].applying(trans) } } /// Transforms the given array of touch points (pixels) into values on the chart. open func pixelsToValues(_ pixels: inout [CGPoint]) { let trans = pixelToValueMatrix for i in 0 ..< pixels.count { pixels[i] = pixels[i].applying(trans) } } /// Transforms the given touch point (pixels) into a value on the chart. open func pixelToValues(_ pixel: inout CGPoint) { pixel = pixel.applying(pixelToValueMatrix) } /// - returns: The x and y values in the chart at the given touch point /// (encapsulated in a CGPoint). This method transforms pixel coordinates to /// coordinates / values in the chart. @objc open func valueForTouchPoint(_ point: CGPoint) -> CGPoint { return point.applying(pixelToValueMatrix) } /// - returns: The x and y values in the chart at the given touch point /// (x/y). This method transforms pixel coordinates to /// coordinates / values in the chart. @objc open func valueForTouchPoint(x: CGFloat, y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y).applying(pixelToValueMatrix) } @objc open var valueToPixelMatrix: CGAffineTransform { return _matrixValueToPx.concatenating(_viewPortHandler.touchMatrix ).concatenating(_matrixOffset ) } @objc open var pixelToValueMatrix: CGAffineTransform { return valueToPixelMatrix.inverted() } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/TransformerHorizontalBarChart.swift ================================================ // // TransformerHorizontalBarChart.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics @objc(ChartTransformerHorizontalBarChart) open class TransformerHorizontalBarChart: Transformer { /// Prepares the matrix that contains all offsets. open override func prepareMatrixOffset(inverted: Bool) { if !inverted { _matrixOffset = CGAffineTransform(translationX: _viewPortHandler.offsetLeft, y: _viewPortHandler.chartHeight - _viewPortHandler.offsetBottom) } else { _matrixOffset = CGAffineTransform(scaleX: -1.0, y: 1.0) _matrixOffset = _matrixOffset.translatedBy(x: -(_viewPortHandler.chartWidth - _viewPortHandler.offsetRight), y: _viewPortHandler.chartHeight - _viewPortHandler.offsetBottom) } } } ================================================ FILE: Pods/Charts/Source/Charts/Utils/ViewPortHandler.swift ================================================ // // ViewPortHandler.swift // Charts // // Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda // A port of MPAndroidChart for iOS // Licensed under Apache License 2.0 // // https://github.com/danielgindi/Charts // import Foundation import CoreGraphics /// Class that contains information about the charts current viewport settings, including offsets, scale & translation levels, ... @objc(ChartViewPortHandler) open class ViewPortHandler: NSObject { /// matrix used for touch events private var _touchMatrix = CGAffineTransform.identity /// this rectangle defines the area in which graph values can be drawn private var _contentRect = CGRect() private var _chartWidth = CGFloat(0.0) private var _chartHeight = CGFloat(0.0) /// minimum scale value on the y-axis private var _minScaleY = CGFloat(1.0) /// maximum scale value on the y-axis private var _maxScaleY = CGFloat.greatestFiniteMagnitude /// minimum scale value on the x-axis private var _minScaleX = CGFloat(1.0) /// maximum scale value on the x-axis private var _maxScaleX = CGFloat.greatestFiniteMagnitude /// contains the current scale factor of the x-axis private var _scaleX = CGFloat(1.0) /// contains the current scale factor of the y-axis private var _scaleY = CGFloat(1.0) /// current translation (drag distance) on the x-axis private var _transX = CGFloat(0.0) /// current translation (drag distance) on the y-axis private var _transY = CGFloat(0.0) /// offset that allows the chart to be dragged over its bounds on the x-axis private var _transOffsetX = CGFloat(0.0) /// offset that allows the chart to be dragged over its bounds on the x-axis private var _transOffsetY = CGFloat(0.0) /// Constructor - don't forget calling setChartDimens(...) @objc public init(width: CGFloat, height: CGFloat) { super.init() setChartDimens(width: width, height: height) } @objc open func setChartDimens(width: CGFloat, height: CGFloat) { let offsetLeft = self.offsetLeft let offsetTop = self.offsetTop let offsetRight = self.offsetRight let offsetBottom = self.offsetBottom _chartHeight = height _chartWidth = width restrainViewPort(offsetLeft: offsetLeft, offsetTop: offsetTop, offsetRight: offsetRight, offsetBottom: offsetBottom) } @objc open var hasChartDimens: Bool { if _chartHeight > 0.0 && _chartWidth > 0.0 { return true } else { return false } } @objc open func restrainViewPort(offsetLeft: CGFloat, offsetTop: CGFloat, offsetRight: CGFloat, offsetBottom: CGFloat) { _contentRect.origin.x = offsetLeft _contentRect.origin.y = offsetTop _contentRect.size.width = _chartWidth - offsetLeft - offsetRight _contentRect.size.height = _chartHeight - offsetBottom - offsetTop } @objc open var offsetLeft: CGFloat { return _contentRect.origin.x } @objc open var offsetRight: CGFloat { return _chartWidth - _contentRect.size.width - _contentRect.origin.x } @objc open var offsetTop: CGFloat { return _contentRect.origin.y } @objc open var offsetBottom: CGFloat { return _chartHeight - _contentRect.size.height - _contentRect.origin.y } @objc open var contentTop: CGFloat { return _contentRect.origin.y } @objc open var contentLeft: CGFloat { return _contentRect.origin.x } @objc open var contentRight: CGFloat { return _contentRect.origin.x + _contentRect.size.width } @objc open var contentBottom: CGFloat { return _contentRect.origin.y + _contentRect.size.height } @objc open var contentWidth: CGFloat { return _contentRect.size.width } @objc open var contentHeight: CGFloat { return _contentRect.size.height } @objc open var contentRect: CGRect { return _contentRect } @objc open var contentCenter: CGPoint { return CGPoint(x: _contentRect.origin.x + _contentRect.size.width / 2.0, y: _contentRect.origin.y + _contentRect.size.height / 2.0) } @objc open var chartHeight: CGFloat { return _chartHeight } @objc open var chartWidth: CGFloat { return _chartWidth } // MARK: - Scaling/Panning etc. /// Zooms by the specified zoom factors. @objc open func zoom(scaleX: CGFloat, scaleY: CGFloat) -> CGAffineTransform { return _touchMatrix.scaledBy(x: scaleX, y: scaleY) } /// Zooms around the specified center @objc open func zoom(scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) -> CGAffineTransform { var matrix = _touchMatrix.translatedBy(x: x, y: y) matrix = matrix.scaledBy(x: scaleX, y: scaleY) matrix = matrix.translatedBy(x: -x, y: -y) return matrix } /// Zooms in by 1.4, x and y are the coordinates (in pixels) of the zoom center. @objc open func zoomIn(x: CGFloat, y: CGFloat) -> CGAffineTransform { return zoom(scaleX: 1.4, scaleY: 1.4, x: x, y: y) } /// Zooms out by 0.7, x and y are the coordinates (in pixels) of the zoom center. @objc open func zoomOut(x: CGFloat, y: CGFloat) -> CGAffineTransform { return zoom(scaleX: 0.7, scaleY: 0.7, x: x, y: y) } /// Zooms out to original size. @objc open func resetZoom() -> CGAffineTransform { return zoom(scaleX: 1.0, scaleY: 1.0, x: 0.0, y: 0.0) } /// Sets the scale factor to the specified values. @objc open func setZoom(scaleX: CGFloat, scaleY: CGFloat) -> CGAffineTransform { var matrix = _touchMatrix matrix.a = scaleX matrix.d = scaleY return matrix } /// Sets the scale factor to the specified values. x and y is pivot. @objc open func setZoom(scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) -> CGAffineTransform { var matrix = _touchMatrix matrix.a = 1.0 matrix.d = 1.0 matrix = matrix.translatedBy(x: x, y: y) matrix = matrix.scaledBy(x: scaleX, y: scaleY) matrix = matrix.translatedBy(x: -x, y: -y) return matrix } /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. @objc open func fitScreen() -> CGAffineTransform { _minScaleX = 1.0 _minScaleY = 1.0 return CGAffineTransform.identity } /// Translates to the specified point. @objc open func translate(pt: CGPoint) -> CGAffineTransform { let translateX = pt.x - offsetLeft let translateY = pt.y - offsetTop let matrix = _touchMatrix.concatenating(CGAffineTransform(translationX: -translateX, y: -translateY)) return matrix } /// Centers the viewport around the specified position (x-index and y-value) in the chart. /// Centering the viewport outside the bounds of the chart is not possible. /// Makes most sense in combination with the setScaleMinima(...) method. @objc open func centerViewPort(pt: CGPoint, chart: ChartViewBase) { let translateX = pt.x - offsetLeft let translateY = pt.y - offsetTop let matrix = _touchMatrix.concatenating(CGAffineTransform(translationX: -translateX, y: -translateY)) refresh(newMatrix: matrix, chart: chart, invalidate: true) } /// call this method to refresh the graph with a given matrix @objc @discardableResult open func refresh(newMatrix: CGAffineTransform, chart: ChartViewBase, invalidate: Bool) -> CGAffineTransform { _touchMatrix = newMatrix // make sure scale and translation are within their bounds limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) chart.setNeedsDisplay() return _touchMatrix } /// limits the maximum scale and X translation of the given matrix private func limitTransAndScale(matrix: inout CGAffineTransform, content: CGRect?) { // min scale-x is 1 _scaleX = min(max(_minScaleX, matrix.a), _maxScaleX) // min scale-y is 1 _scaleY = min(max(_minScaleY, matrix.d), _maxScaleY) var width: CGFloat = 0.0 var height: CGFloat = 0.0 if content != nil { width = content!.width height = content!.height } let maxTransX = -width * (_scaleX - 1.0) _transX = min(max(matrix.tx, maxTransX - _transOffsetX), _transOffsetX) let maxTransY = height * (_scaleY - 1.0) _transY = max(min(matrix.ty, maxTransY + _transOffsetY), -_transOffsetY) matrix.tx = _transX matrix.a = _scaleX matrix.ty = _transY matrix.d = _scaleY } /// Sets the minimum scale factor for the x-axis @objc open func setMinimumScaleX(_ xScale: CGFloat) { var newValue = xScale if newValue < 1.0 { newValue = 1.0 } _minScaleX = newValue limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } /// Sets the maximum scale factor for the x-axis @objc open func setMaximumScaleX(_ xScale: CGFloat) { var newValue = xScale if newValue == 0.0 { newValue = CGFloat.greatestFiniteMagnitude } _maxScaleX = newValue limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } /// Sets the minimum and maximum scale factors for the x-axis @objc open func setMinMaxScaleX(minScaleX: CGFloat, maxScaleX: CGFloat) { var newMin = minScaleX var newMax = maxScaleX if newMin < 1.0 { newMin = 1.0 } if newMax == 0.0 { newMax = CGFloat.greatestFiniteMagnitude } _minScaleX = newMin _maxScaleX = maxScaleX limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } /// Sets the minimum scale factor for the y-axis @objc open func setMinimumScaleY(_ yScale: CGFloat) { var newValue = yScale if newValue < 1.0 { newValue = 1.0 } _minScaleY = newValue limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } /// Sets the maximum scale factor for the y-axis @objc open func setMaximumScaleY(_ yScale: CGFloat) { var newValue = yScale if newValue == 0.0 { newValue = CGFloat.greatestFiniteMagnitude } _maxScaleY = newValue limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } @objc open func setMinMaxScaleY(minScaleY: CGFloat, maxScaleY: CGFloat) { var minScaleY = minScaleY, maxScaleY = maxScaleY if minScaleY < 1.0 { minScaleY = 1.0 } if maxScaleY == 0.0 { maxScaleY = CGFloat.greatestFiniteMagnitude } _minScaleY = minScaleY _maxScaleY = maxScaleY limitTransAndScale(matrix: &_touchMatrix, content: _contentRect) } @objc open var touchMatrix: CGAffineTransform { return _touchMatrix } // MARK: - Boundaries Check @objc open func isInBoundsX(_ x: CGFloat) -> Bool { return isInBoundsLeft(x) && isInBoundsRight(x) } @objc open func isInBoundsY(_ y: CGFloat) -> Bool { return isInBoundsTop(y) && isInBoundsBottom(y) } @objc open func isInBounds(x: CGFloat, y: CGFloat) -> Bool { return isInBoundsX(x) && isInBoundsY(y) } @objc open func isInBoundsLeft(_ x: CGFloat) -> Bool { return _contentRect.origin.x <= x + 1.0 } @objc open func isInBoundsRight(_ x: CGFloat) -> Bool { let x = floor(x * 100.0) / 100.0 return (_contentRect.origin.x + _contentRect.size.width) >= x - 1.0 } @objc open func isInBoundsTop(_ y: CGFloat) -> Bool { return _contentRect.origin.y <= y } @objc open func isInBoundsBottom(_ y: CGFloat) -> Bool { let normalizedY = floor(y * 100.0) / 100.0 return (_contentRect.origin.y + _contentRect.size.height) >= normalizedY } /// - returns: The current x-scale factor @objc open var scaleX: CGFloat { return _scaleX } /// - returns: The current y-scale factor @objc open var scaleY: CGFloat { return _scaleY } /// - returns: The minimum x-scale factor @objc open var minScaleX: CGFloat { return _minScaleX } /// - returns: The minimum y-scale factor @objc open var minScaleY: CGFloat { return _minScaleY } /// - returns: The minimum x-scale factor @objc open var maxScaleX: CGFloat { return _maxScaleX } /// - returns: The minimum y-scale factor @objc open var maxScaleY: CGFloat { return _maxScaleY } /// - returns: The translation (drag / pan) distance on the x-axis @objc open var transX: CGFloat { return _transX } /// - returns: The translation (drag / pan) distance on the y-axis @objc open var transY: CGFloat { return _transY } /// if the chart is fully zoomed out, return true @objc open var isFullyZoomedOut: Bool { return isFullyZoomedOutX && isFullyZoomedOutY } /// - returns: `true` if the chart is fully zoomed out on it's y-axis (vertical). @objc open var isFullyZoomedOutY: Bool { return !(_scaleY > _minScaleY || _minScaleY > 1.0) } /// - returns: `true` if the chart is fully zoomed out on it's x-axis (horizontal). @objc open var isFullyZoomedOutX: Bool { return !(_scaleX > _minScaleX || _minScaleX > 1.0) } /// Set an offset in pixels that allows the user to drag the chart over it's bounds on the x-axis. @objc open func setDragOffsetX(_ offset: CGFloat) { _transOffsetX = offset } /// Set an offset in pixels that allows the user to drag the chart over it's bounds on the y-axis. @objc open func setDragOffsetY(_ offset: CGFloat) { _transOffsetY = offset } /// - returns: `true` if both drag offsets (x and y) are zero or smaller. @objc open var hasNoDragOffset: Bool { return _transOffsetX <= 0.0 && _transOffsetY <= 0.0 } /// - returns: `true` if the chart is not yet fully zoomed out on the x-axis @objc open var canZoomOutMoreX: Bool { return _scaleX > _minScaleX } /// - returns: `true` if the chart is not yet fully zoomed in on the x-axis @objc open var canZoomInMoreX: Bool { return _scaleX < _maxScaleX } /// - returns: `true` if the chart is not yet fully zoomed out on the y-axis @objc open var canZoomOutMoreY: Bool { return _scaleY > _minScaleY } /// - returns: `true` if the chart is not yet fully zoomed in on the y-axis @objc open var canZoomInMoreY: Bool { return _scaleY < _maxScaleY } } ================================================ FILE: Pods/Pods.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 003554553254D35F274111A634D0D2E1 /* ScatterChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484C682104D106ED4EEA9595034B0B5E /* ScatterChartDataSet.swift */; }; 0046C1AB90F4417C15D95D7E270E86F6 /* IBarLineScatterCandleBubbleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007ACE796435033B39D972A7CA33ABEB /* IBarLineScatterCandleBubbleChartDataSet.swift */; }; 00A145728D4E065E52B4FA1E4ED43E12 /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6E3051B875B9C266DCE9E7F900474FD /* Renderer.swift */; }; 084B6340D45ABD3F507E18C9BADF6C20 /* BarLineChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2DD7F63F80F5E8F924D99314B1A0EC9 /* BarLineChartViewBase.swift */; }; 0A3BC570D93DCC4F28B982C8C17E63EF /* CandleStickChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B446DB72E179C73E723D6663B72331 /* CandleStickChartRenderer.swift */; }; 0B55EB9B68BAB7A03A295CB522261BAF /* LineRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94E9D17D1B8563135F9D35E32168A5A /* LineRadarChartDataSet.swift */; }; 0B9D63094D8353F890EC4D3A18A3B8D2 /* AxisBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BDEDE32D6063CEA8268F256367DD8A1 /* AxisBase.swift */; }; 0CC0331802115AEFB065F553BEFC26CA /* BarChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71405DE5EBB0DAD4D8C3E6F37A38F887 /* BarChartDataEntry.swift */; }; 0F89F1E27F45279300296F9BD900C487 /* Fill.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F767B671EEC4586936C5ECFD05EE51D /* Fill.swift */; }; 10ACA6864F5D3570A391788771649410 /* TransformerHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FEBA9D8B13E52514910FC991D0DFC25 /* TransformerHorizontalBarChart.swift */; }; 11C9A467C40E4AB6E6C40394EDB6092F /* Legend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88BF0EDD22B86B7053BB600894F8602F /* Legend.swift */; }; 12F85AFD15CD608E23416A9310E97B47 /* BubbleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48BA456A0A69C1DC19A530843B6A9F /* BubbleChartDataProvider.swift */; }; 13C5C749E0027B88BB7FF0FCC7356226 /* IAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57AEC775324B52FE2C100D64BEB39B31 /* IAxisValueFormatter.swift */; }; 1537742A59DB4D25428098AACF5F625C /* RadarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6216B4437A19C60DD91947242EF95DB /* RadarChartRenderer.swift */; }; 15FBDF351099C60ED7ADF84655008D25 /* YAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E56156DA5E1404A1E9D6C20E729FCB /* YAxis.swift */; }; 18069F821440816E7977B79645A1A304 /* RadarChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4626EF42600E567686581FB4198E4DEC /* RadarChartDataEntry.swift */; }; 1931C6D879091A0C2109B560F302E2C9 /* ScatterChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42422E60D433E9E0F99120F92EB6960 /* ScatterChartDataProvider.swift */; }; 1A90212E5C0883623E8C9EEFC5D61933 /* IFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525492DBE52C9C8B11C212DB8A6D7469 /* IFillFormatter.swift */; }; 1F5DCE18354B48B39D86445A3D0CACDE /* AxisRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FC357067FDF91B681F15DC4BDA24274 /* AxisRendererBase.swift */; }; 1F685D6C872C659CFC6C69113281577C /* ChartColorTemplates.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED235828F622CA8CB815A6FF1233C580 /* ChartColorTemplates.swift */; }; 24EB442B6A20FE35818736630ED27035 /* ILineScatterCandleRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E316EAD5FAC4F627B8C539B9E818BB /* ILineScatterCandleRadarChartDataSet.swift */; }; 29EA31025E6D83C37E0BA4B06A222CB4 /* PieHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC713DA16C24DB30485289E8FFF884B1 /* PieHighlighter.swift */; }; 2CF0DD46EBD523F09A7E9F77A8671FE3 /* PieChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E790CEAF83BFB05C9EBC292A499B8E21 /* PieChartRenderer.swift */; }; 2D7B833632CB3920E0068A7C1F25FE38 /* LineScatterCandleRadarRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78FCA6EF2D206D8FAD19D32068367859 /* LineScatterCandleRadarRenderer.swift */; }; 2E366CD79E7A871563B427DDF15FA676 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; 2F958D66B40343652DC2A08CB3236C9A /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165DBA3CCC4338298A6DF2DE448F507A /* Platform.swift */; }; 2FB5A6CAC10BF11298827B4967922E16 /* ScatterChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD8827780795878F316BFE265FA3A4E /* ScatterChartRenderer.swift */; }; 30AB361CB3D892588576FEC790654E12 /* BarHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C02B97735E54C7C4B8D5C500C701AC6D /* BarHighlighter.swift */; }; 34BAA315419AB4D8EE2719368B03854A /* ChartAnimationEasing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29DB4C76953164FFEE710830C4831670 /* ChartAnimationEasing.swift */; }; 360971EA6BC84CE3255F0B0E15FA41C3 /* TriangleShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33EE377C802C9F4F19B0AC65814351DD /* TriangleShapeRenderer.swift */; }; 37F1A09C1DFE65A585B6F7E1067A738F /* DataApproximator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFCF5BE4C67A4A874C3137F15DC58D6 /* DataApproximator.swift */; }; 3833B92370E686B9CEED0755059A1C29 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; 3B5EFB86415AC3FEDF5D9A7D02DE62F9 /* Charts-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 67C4A2A1421AE830E9071E84133869BA /* Charts-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3C023F772442591F73DEECDF1E0BCBB6 /* Pods-ChartsUnderstandAndUsage-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EEBA64CD2F1E7C2BB8CDCFDC9DA28BAF /* Pods-ChartsUnderstandAndUsage-dummy.m */; }; 3C039B985E79F6F849BD19D66B6C60A5 /* LegendRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A0BC2B5325B768A61A9B412A7C5CCA /* LegendRenderer.swift */; }; 3F6937EECDD01987607446AF2CBB1A9E /* DataApproximator+N.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786A9572EE474CD5AD3AA027402F7B3C /* DataApproximator+N.swift */; }; 400BF641FF5A5D6D658CFEF486539C8E /* ChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19BB904A3F49CD6633D8578419F2C44E /* ChartDataEntry.swift */; }; 4382960229B403F2A50C4AD1AC9D7A5A /* Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1AB697393A4A48A88649EA3E5BB49CF /* Description.swift */; }; 45C99C760AB49ED28A4DD956AD29953B /* CandleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E64E474C4D9C0766155F26F7D296E9 /* CandleChartDataSet.swift */; }; 483DA7418A3DD9D979793424ACA415A8 /* CircleShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DD30AB1BFD8A3AEA97BA9D336D944B5 /* CircleShapeRenderer.swift */; }; 484F0E87FE13B0C3754B1622784DCBE3 /* SquareShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE145A9E4312F162671A36DAEEB21250 /* SquareShapeRenderer.swift */; }; 4917571E1362E48CAD513F065882CE49 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82AE4E5B6D9F12829FB2BB33E0AB7C37 /* Highlight.swift */; }; 4A98B4A980615E01C1432CDDDF029118 /* MarkerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAF7777FE62D3C1EB5630B94E0C156C2 /* MarkerView.swift */; }; 4C8D108F68647323A8B42E3E55062050 /* ScatterChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A6EFA9EFCEF0A9928EA2C44398166C /* ScatterChartView.swift */; }; 4E3A1CC25A3E9136B8AA354099970541 /* BarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEA77C52B1FD92FF2B32F140E02FAB95 /* BarChartData.swift */; }; 4FB99CA582685A45E97AF7B3AF7933B1 /* LineRadarRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F10E9673F9B77BAB3587661DA9FA16DE /* LineRadarRenderer.swift */; }; 50418844F42352CED365ADD7E0270AC4 /* HorizontalBarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6F19E729FE0680BF24796BE4015725A /* HorizontalBarChartRenderer.swift */; }; 513BEDD62A390D06A6E94D4FD8B0F302 /* LineChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A7A5CC7697745714306BD2690C79C8 /* LineChartRenderer.swift */; }; 522608D640101E73C800C9DE6EF8FB45 /* ChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B04FC73DEE651FCB3CA890B1887530 /* ChartHighlighter.swift */; }; 5248BCD7057BD67A7036E01B5B79AED6 /* ViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC25025089C77D41976265C4F0F58E8 /* ViewPortJob.swift */; }; 539FD377F21414A041563D5D36F9B88C /* LineScatterCandleRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2EDF10F582435C12DCB45001619373 /* LineScatterCandleRadarChartDataSet.swift */; }; 54EFBF5D59D629C859C8737F55F0371D /* ZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E21D64E16B230057560FD9B171417D /* ZoomViewJob.swift */; }; 550BB7099D4BF13B788AF99C5CF7DBB2 /* ChartDataEntryBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 598CD5E177243E5C0F91B307CB95AE84 /* ChartDataEntryBase.swift */; }; 55377CD1DE87856B328CE723D7723BEC /* IRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D254093DBD4DA34954D729B1104D048 /* IRadarChartDataSet.swift */; }; 559AE6BC5D7AFAF699C1B689474F5FA0 /* ChartDataRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58038678BB787C725CAB1D92B973C65 /* ChartDataRendererBase.swift */; }; 5760D9C025FA510EABB7FC28FD9A8F25 /* XAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2323EE20AC1EF48B712A20DBF6494B /* XAxisRendererRadarChart.swift */; }; 57A0E17849CB58C8AEFCF6ED0EFD6E45 /* PieChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008321D48AF3972C2E4DFAA4F8D01E81 /* PieChartDataSet.swift */; }; 599767D881AEB44D6C83BFFD56A87E58 /* CombinedChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15027A836EB4E05B5BF6AF01E1C18561 /* CombinedChartRenderer.swift */; }; 5D06EECF467542A799500755ACCFE6B2 /* ChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 494AFC909175F5F2B1FA37AEE828D5E1 /* ChartDataProvider.swift */; }; 6066C3E705478F5CFE95FEB766FA3BDC /* ChartBaseDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED02C3CCA3B55AE99E9DC95CF505D036 /* ChartBaseDataSet.swift */; }; 616234121EDE60031BA2CA550BF3429E /* RadarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AE450AA18DB25C104B10D00CCBF997 /* RadarChartData.swift */; }; 61A60DC3211280217C1FB1CE311D357B /* IMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2D5024C31D8A9C9AB00B1A04C4FAFF /* IMarker.swift */; }; 625AF0CE4C4446338C9EB59A815ABDC9 /* IShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8B72865ACD867C65FC699940A86205 /* IShapeRenderer.swift */; }; 635B44240C7EA306309A9112E2884214 /* BubbleChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453494AC5B704315FD7793D0D394F663 /* BubbleChartDataEntry.swift */; }; 63D4F653030715FB89B82AE6B87861EA /* ViewPortHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2544BD484C3F5ABB4EAD6CE6B53130D /* ViewPortHandler.swift */; }; 662ABB1F60EFAF4C8FC7063DE4CB8B16 /* Charts-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD0A0893387A7D417C7290B39A7AF86 /* Charts-dummy.m */; }; 678061020C1872E749B279665E11DB43 /* AnimatedZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F7305A3B93E6041536BB02BB80B480 /* AnimatedZoomViewJob.swift */; }; 68622CB4D5CFD97D4F097024476451BD /* LineChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46427C7A4AA6AD8C4D3519E28915663 /* LineChartData.swift */; }; 699AD84F8DCE24C54A47FD3E6AC2C363 /* CandleChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10965CFD80A8625FF0880652EA75316 /* CandleChartDataEntry.swift */; }; 6A5F4AB33903B7A22D7504B0A368E871 /* IndexAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D98175BB36E4F7BEF08576940853429 /* IndexAxisValueFormatter.swift */; }; 6A96E3270BCFF7B8583533A2CA6BA15A /* XAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657F9EE6FC7DE799B1718E62EA2C853E /* XAxis.swift */; }; 6AFA62D77EA662A8B783850FE2F1FEA2 /* IBubbleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A953D981FE4D97652B09FEBB96411948 /* IBubbleChartDataSet.swift */; }; 6BD08D93E6B3EFA0271413456E10419E /* CombinedChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A70BCDA190E40E078ECCB211F647DDE /* CombinedChartData.swift */; }; 709F5F7B52E7F4F7E0B9E8F8B0D06D14 /* ILineRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B19DFF38B1079F518E3F07E75BC3C6 /* ILineRadarChartDataSet.swift */; }; 716C88D975F6E34C75F89BF4D0F2AEE4 /* XAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = D577C0815236E87C6122880D5A9ED9C0 /* XAxisRendererHorizontalBarChart.swift */; }; 724C6942E911FAD8F02E2049C06CEF0C /* Pods-ChartsUnderstandAndUsage-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F5BF0B4B02C0E09EC7383F4F4A80F2C7 /* Pods-ChartsUnderstandAndUsage-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 73D7B5895D94797825C3F74FF95E2BE1 /* BarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB662CBF72CFD63348D2BB4AB2E985 /* BarChartRenderer.swift */; }; 7415DBDD6B7746AC4F20CE7834501B04 /* HorizontalBarHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA27DD30748D67E97A6CED2F2870AFFA /* HorizontalBarHighlighter.swift */; }; 75D6AAC3F5C75B4AD6B790BF8676C138 /* ScatterChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9997A233D1B97B14EC5DD2E669F98A6 /* ScatterChartData.swift */; }; 76BAF63CC685FC732F6EBD277050D62A /* LineChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44D9EB04F3E3D05CFAF943DD7C584D35 /* LineChartView.swift */; }; 76C01F136C752E2ACAD315A7176EDFF2 /* ChevronDownShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96ED88D65AF34AED73B86599F51B3715 /* ChevronDownShapeRenderer.swift */; }; 79A2AAACDF64DE2DB862910BCEE6F1CC /* MarkerImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFEF8702759A1E45A5306231BDD8533 /* MarkerImage.swift */; }; 7ACC2B5EA9D1AE5F69EAA9EBF83A67BD /* LegendEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FCB5ADD20BE774EC67978F8CFA2BFB /* LegendEntry.swift */; }; 842046A543F7B928A7A2F1673AD50EDE /* MoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D664CB09A2C251499554D5B63A0B442 /* MoveViewJob.swift */; }; 88F85B9CE41D86B8356B0958BF303897 /* CandleChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C64BD2349360E0B3C3CA75B0BD3894E /* CandleChartData.swift */; }; 8AF733E74DCD1B2573B69ADDA1A27714 /* BarChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CC9A2A4F739DA1AE4752907BFDF35D /* BarChartDataProvider.swift */; }; 8B89F523F97C8FEA7E1ABDDDC70DC93D /* RadarHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A10B71171CCD62F9CCC058D02D5486 /* RadarHighlighter.swift */; }; 8E50FEF8873FA6756E05749D46DD623E /* PieRadarChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87871D0FE0AA14E98C678F0C8D38738 /* PieRadarChartViewBase.swift */; }; 90E4A90C5A172097D1DF30DABB7FC67C /* PieRadarHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A81033F1455249B76D41A872F01834 /* PieRadarHighlighter.swift */; }; 9258C60F7F2E419A812E54116650E228 /* BubbleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4A94014E26341A7B22762740ACA5FE /* BubbleChartDataSet.swift */; }; 93E80ACA163EE24045B4851AE570BB1F /* IHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0E7AE74D531F333CB8FC4A9DA1616F /* IHighlighter.swift */; }; 9561A80B9A4A4780175A2D482F366D12 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D30042C23B309BE8FA1105797B3881 /* Transformer.swift */; }; 9567217C2831B5AA047F5E05C2343E44 /* ChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812AD0F04F4695E01AD8F2FC2CAA2718 /* ChartUtils.swift */; }; 9B40DB5E7B2FF344B275F9666A4B7188 /* CandleStickChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D18D13C8A9A9FEB7606A1D86E5FC9 /* CandleStickChartView.swift */; }; 9C525A5B546DB356A60BC24C5FD2ADDA /* BubbleChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB79A7DB9033B56511A7B7B8AE447F82 /* BubbleChartView.swift */; }; 9CE18CD1A6115AF2CBAFA8A7680CF2EF /* BarLineScatterCandleBubbleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAEAEBADF1D0FACFB05A283ABD9F289 /* BarLineScatterCandleBubbleChartDataSet.swift */; }; A6D06811E3E52D1A586FCEE1BB8E7CF9 /* CandleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8028C080C73771E040542C877894F98 /* CandleChartDataProvider.swift */; }; AC045E2F3D22FE57B16A071657902A1D /* RadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A54F9811E788CA25C899C22FC8CDD9 /* RadarChartDataSet.swift */; }; B0282A8E32E1E1448A731E2DE9B4F16C /* AnimatedViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D55BAD3DB826448025A7E587A7B7CC8 /* AnimatedViewPortJob.swift */; }; B0A6783A037B5D31DB686EE4E9B31F0E /* AnimatedMoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B0DE3264F632D6FE8BDE7528540564 /* AnimatedMoveViewJob.swift */; }; B4F6F63AFBA13608371C01D774BF8624 /* IBarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C93047F5B2F06C72B285A6A145A3A199 /* IBarChartDataSet.swift */; }; B622420C87D8B10622F0251F1DB43539 /* ChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4820C006BD5F2D6BC446058D5F26DEB /* ChartViewBase.swift */; }; B786863F4221D6DB4F04C7AF61CC70DA /* PieChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A1FC4B24CEC527E730D3ED4BD08AC7 /* PieChartData.swift */; }; BAA7516B212056F1244ED08788690AEB /* PieChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E88B24545A64CA0048CC9EBA7C5BBE4D /* PieChartDataEntry.swift */; }; BAB483EF709006D533591BD66064BB80 /* YAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665E8BD2EA3EF1FC2604151E27ED6514 /* YAxisRenderer.swift */; }; BBAB4A8FC86B70A2A5D8D18B332426F7 /* YAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21FF3D59CCA9526A1259CE00C4F4FD5 /* YAxisRendererRadarChart.swift */; }; BF02CF3DCB633921EB6F7D38321944C1 /* YAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE04063011E2AA67BCB09C07D709F201 /* YAxisRendererHorizontalBarChart.swift */; }; C0262EC2FBB976BA224509F09A161E8B /* BubbleChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E727EB214E6927C9256929A069BDDA84 /* BubbleChartRenderer.swift */; }; C07BCF3B9148133703F40DDEC2AC0916 /* ChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F340AC726041C97BCA3EEF9607250BEC /* ChartData.swift */; }; C07C7204DB8269F399C39E0247E7143C /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113774AEFE9BEF3248173AD2D14C9494 /* Range.swift */; }; C69BFCE003CD359A9A19D06947B396C6 /* CombinedHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB7893D7FA975910BD8B4C7E91DBC0FF /* CombinedHighlighter.swift */; }; CCB2247822E46CE23C53D36F72C807E9 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E721F418A914A0F3EBEC3E53CB14935 /* Animator.swift */; }; CCEC0460147ED3D1C065D34AFAAE1983 /* BarLineScatterCandleBubbleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B629D8CD1EDD2268CF1DFCDAE865B0 /* BarLineScatterCandleBubbleRenderer.swift */; }; CE3A62A1E007ECFDF50113D59DA80994 /* XAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7019B7E6DB7EDB6E22A0CF9AC96BAD2F /* XAxisRenderer.swift */; }; D1E91C022180D9035B715F1FCFE92801 /* CrossShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0427EF510D6604A39DEB0E64750BDB30 /* CrossShapeRenderer.swift */; }; D3AFA3E484882E0AF08FA2EB2B7E4D22 /* ICandleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD494D5B4708D79817062A80D445CFCB /* ICandleChartDataSet.swift */; }; D647539D105E5808D02B2F7B831AFF03 /* ChartLimitLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA781C0D80DD82A580DEB8A1255DD74 /* ChartLimitLine.swift */; }; D7AA6CF3FFC9783346A2899FF8100161 /* ILineChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EADD8733404CC75894D869C9048EAEA /* ILineChartDataSet.swift */; }; D8708EC587000DA1C24D7CB760B5829D /* LineChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C45599881349FE4360659D131EA8AC /* LineChartDataProvider.swift */; }; D90C8B456D4DEA6A52CF1CE4C22214B0 /* BarLineScatterCandleBubbleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A603D6F6A5951453D13EFDFBFFF3D /* BarLineScatterCandleBubbleChartDataProvider.swift */; }; D927DDE6750131B8B5BFD4CD1473C1F7 /* BarLineScatterCandleBubbleChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 258EDF70931EB74813A2DD320306BA33 /* BarLineScatterCandleBubbleChartData.swift */; }; DB78D0054C513B970A441A329A6C85CF /* IChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9953CC83D581E416F0DA65D242F29215 /* IChartDataSet.swift */; }; DD2F99BB6E7F20B2CCF97A72B512C8E6 /* IScatterChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B454498F318F86C7DB23A7E85DC47EBE /* IScatterChartDataSet.swift */; }; DDB6AB1A86E9233A63F845939F40FE88 /* CombinedChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3A6B5F94C303A2379A82A4450D2866 /* CombinedChartDataProvider.swift */; }; DE224D37757460451813B12885B9A08C /* CombinedChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28E90F74B7709CECB701B57153C9B90B /* CombinedChartView.swift */; }; E126E9E22A04ACBA264DAE51CC186F2F /* DefaultAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2118193BDC7D7F1EAA7D8158425643C /* DefaultAxisValueFormatter.swift */; }; E12E7BA51F8E7FC3649AB6F644313036 /* LineChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2C6184611E77B22F25874CC8B74BE46 /* LineChartDataSet.swift */; }; E5DFB46E97D8872DC7FC1B99D796FDCE /* IValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2645E6CA6C5CFE4378102FE21D376B85 /* IValueFormatter.swift */; }; E77FDA7A004007FDD84B3C1DE2AB851C /* DefaultFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80917B53E0AE415F6EAD4B11B543E7C /* DefaultFillFormatter.swift */; }; E83897772D05EBC9225C8944F42F169B /* BubbleChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D6DCA547E4BA53F4B3C052C622EC13D /* BubbleChartData.swift */; }; E9A302DEB341C0585984FFB722F86508 /* DefaultValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB224F2962F270CFFF70379E5E71B373 /* DefaultValueFormatter.swift */; }; EA1A7688D149774765033557088632C3 /* XShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB3B0DA334A74D1E6D3F1A16B78285D9 /* XShapeRenderer.swift */; }; F087F143628157125CD0B5572AE784AD /* IPieChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F59E68FA6685EBB93531B0F6D1AD184 /* IPieChartDataSet.swift */; }; F2DAF14754E7BCAD4BEA1E4BE5F46C40 /* BarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 929872A625629FEAAA6A03279FB743E1 /* BarChartView.swift */; }; F6C7C354E96D0D0AFCE1A1A952EF946F /* ChevronUpShapeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990C40B4FA90E062D44AEE2B861355F6 /* ChevronUpShapeRenderer.swift */; }; F98383377531639D2890A7588F148DAF /* HorizontalBarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40F6967D5284C16C29D9616E9082AF73 /* HorizontalBarChartView.swift */; }; FC68B5BE8559E7CEAE321C78AC267D3D /* ChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D649DF87AB0C9EF8A7D4131698A2EA /* ChartDataSet.swift */; }; FD3792C290ADDE889980FAE5E3B3D45E /* PieChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3994AB0FE4D3C98A5DC0F47EC1D36E86 /* PieChartView.swift */; }; FD68C7F7AAA4C11EDFBCE1EB61AF3978 /* RadarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A014915174EB90A68FDB219160768B6D /* RadarChartView.swift */; }; FEA1B96EEE907DCE04685151022AD6B0 /* BarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECDEDA2CD5334E58AF14669B92154AFB /* BarChartDataSet.swift */; }; FED28187B78D99CC0AFE568A2DEE625B /* ComponentBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209A5D2750113D9169AC70DE5A4FBB77 /* ComponentBase.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 53082718155BA30158B8DBD2D1315F84 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = BF03B0B2B8B052092CB5F90CC5FB3757; remoteInfo = Charts; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 007ACE796435033B39D972A7CA33ABEB /* IBarLineScatterCandleBubbleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IBarLineScatterCandleBubbleChartDataSet.swift; path = Source/Charts/Data/Interfaces/IBarLineScatterCandleBubbleChartDataSet.swift; sourceTree = ""; }; 008321D48AF3972C2E4DFAA4F8D01E81 /* PieChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/PieChartDataSet.swift; sourceTree = ""; }; 00CB662CBF72CFD63348D2BB4AB2E985 /* BarChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartRenderer.swift; path = Source/Charts/Renderers/BarChartRenderer.swift; sourceTree = ""; }; 0394FC7CC3C3816823F09785CDB203C6 /* Charts.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Charts.release.xcconfig; sourceTree = ""; }; 0427EF510D6604A39DEB0E64750BDB30 /* CrossShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CrossShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/CrossShapeRenderer.swift; sourceTree = ""; }; 08FCB5ADD20BE774EC67978F8CFA2BFB /* LegendEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LegendEntry.swift; path = Source/Charts/Components/LegendEntry.swift; sourceTree = ""; }; 0CD8827780795878F316BFE265FA3A4E /* ScatterChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartRenderer.swift; path = Source/Charts/Renderers/ScatterChartRenderer.swift; sourceTree = ""; }; 0F59E68FA6685EBB93531B0F6D1AD184 /* IPieChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IPieChartDataSet.swift; path = Source/Charts/Data/Interfaces/IPieChartDataSet.swift; sourceTree = ""; }; 0FC357067FDF91B681F15DC4BDA24274 /* AxisRendererBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AxisRendererBase.swift; path = Source/Charts/Renderers/AxisRendererBase.swift; sourceTree = ""; }; 0FEBA9D8B13E52514910FC991D0DFC25 /* TransformerHorizontalBarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformerHorizontalBarChart.swift; path = Source/Charts/Utils/TransformerHorizontalBarChart.swift; sourceTree = ""; }; 113774AEFE9BEF3248173AD2D14C9494 /* Range.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Range.swift; path = Source/Charts/Highlight/Range.swift; sourceTree = ""; }; 15027A836EB4E05B5BF6AF01E1C18561 /* CombinedChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombinedChartRenderer.swift; path = Source/Charts/Renderers/CombinedChartRenderer.swift; sourceTree = ""; }; 165DBA3CCC4338298A6DF2DE448F507A /* Platform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Platform.swift; path = Source/Charts/Utils/Platform.swift; sourceTree = ""; }; 19BB904A3F49CD6633D8578419F2C44E /* ChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/ChartDataEntry.swift; sourceTree = ""; }; 1A3A6B5F94C303A2379A82A4450D2866 /* CombinedChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombinedChartDataProvider.swift; path = Source/Charts/Interfaces/CombinedChartDataProvider.swift; sourceTree = ""; }; 1D98175BB36E4F7BEF08576940853429 /* IndexAxisValueFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IndexAxisValueFormatter.swift; path = Source/Charts/Formatters/IndexAxisValueFormatter.swift; sourceTree = ""; }; 209A5D2750113D9169AC70DE5A4FBB77 /* ComponentBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ComponentBase.swift; path = Source/Charts/Components/ComponentBase.swift; sourceTree = ""; }; 258EDF70931EB74813A2DD320306BA33 /* BarLineScatterCandleBubbleChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarLineScatterCandleBubbleChartData.swift; path = Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift; sourceTree = ""; }; 2645E6CA6C5CFE4378102FE21D376B85 /* IValueFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IValueFormatter.swift; path = Source/Charts/Formatters/IValueFormatter.swift; sourceTree = ""; }; 27B0DE3264F632D6FE8BDE7528540564 /* AnimatedMoveViewJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedMoveViewJob.swift; path = Source/Charts/Jobs/AnimatedMoveViewJob.swift; sourceTree = ""; }; 28E90F74B7709CECB701B57153C9B90B /* CombinedChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombinedChartView.swift; path = Source/Charts/Charts/CombinedChartView.swift; sourceTree = ""; }; 29AE450AA18DB25C104B10D00CCBF997 /* RadarChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarChartData.swift; path = Source/Charts/Data/Implementations/Standard/RadarChartData.swift; sourceTree = ""; }; 29DB4C76953164FFEE710830C4831670 /* ChartAnimationEasing.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartAnimationEasing.swift; path = Source/Charts/Animation/ChartAnimationEasing.swift; sourceTree = ""; }; 2B2EDF10F582435C12DCB45001619373 /* LineScatterCandleRadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineScatterCandleRadarChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/LineScatterCandleRadarChartDataSet.swift; sourceTree = ""; }; 33EE377C802C9F4F19B0AC65814351DD /* TriangleShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TriangleShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/TriangleShapeRenderer.swift; sourceTree = ""; }; 37A0BC2B5325B768A61A9B412A7C5CCA /* LegendRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LegendRenderer.swift; path = Source/Charts/Renderers/LegendRenderer.swift; sourceTree = ""; }; 388A603D6F6A5951453D13EFDFBFFF3D /* BarLineScatterCandleBubbleChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarLineScatterCandleBubbleChartDataProvider.swift; path = Source/Charts/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift; sourceTree = ""; }; 38D649DF87AB0C9EF8A7D4131698A2EA /* ChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/ChartDataSet.swift; sourceTree = ""; }; 3994AB0FE4D3C98A5DC0F47EC1D36E86 /* PieChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieChartView.swift; path = Source/Charts/Charts/PieChartView.swift; sourceTree = ""; }; 3D4A94014E26341A7B22762740ACA5FE /* BubbleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/BubbleChartDataSet.swift; sourceTree = ""; }; 3D664CB09A2C251499554D5B63A0B442 /* MoveViewJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MoveViewJob.swift; path = Source/Charts/Jobs/MoveViewJob.swift; sourceTree = ""; }; 3D6DCA547E4BA53F4B3C052C622EC13D /* BubbleChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartData.swift; path = Source/Charts/Data/Implementations/Standard/BubbleChartData.swift; sourceTree = ""; }; 40F6967D5284C16C29D9616E9082AF73 /* HorizontalBarChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HorizontalBarChartView.swift; path = Source/Charts/Charts/HorizontalBarChartView.swift; sourceTree = ""; }; 44D9EB04F3E3D05CFAF943DD7C584D35 /* LineChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartView.swift; path = Source/Charts/Charts/LineChartView.swift; sourceTree = ""; }; 453494AC5B704315FD7793D0D394F663 /* BubbleChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/BubbleChartDataEntry.swift; sourceTree = ""; }; 4626EF42600E567686581FB4198E4DEC /* RadarChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/RadarChartDataEntry.swift; sourceTree = ""; }; 47B19DFF38B1079F518E3F07E75BC3C6 /* ILineRadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ILineRadarChartDataSet.swift; path = Source/Charts/Data/Interfaces/ILineRadarChartDataSet.swift; sourceTree = ""; }; 47E21D64E16B230057560FD9B171417D /* ZoomViewJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ZoomViewJob.swift; path = Source/Charts/Jobs/ZoomViewJob.swift; sourceTree = ""; }; 484C682104D106ED4EEA9595034B0B5E /* ScatterChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/ScatterChartDataSet.swift; sourceTree = ""; }; 494AFC909175F5F2B1FA37AEE828D5E1 /* ChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartDataProvider.swift; path = Source/Charts/Interfaces/ChartDataProvider.swift; sourceTree = ""; }; 4A70BCDA190E40E078ECCB211F647DDE /* CombinedChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombinedChartData.swift; path = Source/Charts/Data/Implementations/Standard/CombinedChartData.swift; sourceTree = ""; }; 4CFEF8702759A1E45A5306231BDD8533 /* MarkerImage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MarkerImage.swift; path = Source/Charts/Components/MarkerImage.swift; sourceTree = ""; }; 5144D14E205EDED26DC3F8DC591EB3A2 /* Pods_ChartsUnderstandAndUsage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_ChartsUnderstandAndUsage.framework; path = "Pods-ChartsUnderstandAndUsage.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; 51E64E474C4D9C0766155F26F7D296E9 /* CandleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/CandleChartDataSet.swift; sourceTree = ""; }; 525492DBE52C9C8B11C212DB8A6D7469 /* IFillFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IFillFormatter.swift; path = Source/Charts/Formatters/IFillFormatter.swift; sourceTree = ""; }; 57AEC775324B52FE2C100D64BEB39B31 /* IAxisValueFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IAxisValueFormatter.swift; path = Source/Charts/Formatters/IAxisValueFormatter.swift; sourceTree = ""; }; 598CD5E177243E5C0F91B307CB95AE84 /* ChartDataEntryBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartDataEntryBase.swift; path = Source/Charts/Data/Implementations/Standard/ChartDataEntryBase.swift; sourceTree = ""; }; 5D55BAD3DB826448025A7E587A7B7CC8 /* AnimatedViewPortJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedViewPortJob.swift; path = Source/Charts/Jobs/AnimatedViewPortJob.swift; sourceTree = ""; }; 5DD30AB1BFD8A3AEA97BA9D336D944B5 /* CircleShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CircleShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/CircleShapeRenderer.swift; sourceTree = ""; }; 5EADD8733404CC75894D869C9048EAEA /* ILineChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ILineChartDataSet.swift; path = Source/Charts/Data/Interfaces/ILineChartDataSet.swift; sourceTree = ""; }; 607EF751D5BAB9BCA337430FDC584C14 /* Pods-ChartsUnderstandAndUsage-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ChartsUnderstandAndUsage-acknowledgements.plist"; sourceTree = ""; }; 63A1FC4B24CEC527E730D3ED4BD08AC7 /* PieChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieChartData.swift; path = Source/Charts/Data/Implementations/Standard/PieChartData.swift; sourceTree = ""; }; 657F9EE6FC7DE799B1718E62EA2C853E /* XAxis.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XAxis.swift; path = Source/Charts/Components/XAxis.swift; sourceTree = ""; }; 665E8BD2EA3EF1FC2604151E27ED6514 /* YAxisRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = YAxisRenderer.swift; path = Source/Charts/Renderers/YAxisRenderer.swift; sourceTree = ""; }; 67C4A2A1421AE830E9071E84133869BA /* Charts-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Charts-umbrella.h"; sourceTree = ""; }; 6F6C51CB6D62B862D7993889D948085C /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ChartsUnderstandAndUsage.debug.xcconfig"; sourceTree = ""; }; 7019B7E6DB7EDB6E22A0CF9AC96BAD2F /* XAxisRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XAxisRenderer.swift; path = Source/Charts/Renderers/XAxisRenderer.swift; sourceTree = ""; }; 71405DE5EBB0DAD4D8C3E6F37A38F887 /* BarChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/BarChartDataEntry.swift; sourceTree = ""; }; 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 786A9572EE474CD5AD3AA027402F7B3C /* DataApproximator+N.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DataApproximator+N.swift"; path = "Source/Charts/Filters/DataApproximator+N.swift"; sourceTree = ""; }; 78FCA6EF2D206D8FAD19D32068367859 /* LineScatterCandleRadarRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineScatterCandleRadarRenderer.swift; path = Source/Charts/Renderers/LineScatterCandleRadarRenderer.swift; sourceTree = ""; }; 7AA781C0D80DD82A580DEB8A1255DD74 /* ChartLimitLine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartLimitLine.swift; path = Source/Charts/Components/ChartLimitLine.swift; sourceTree = ""; }; 7C64BD2349360E0B3C3CA75B0BD3894E /* CandleChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleChartData.swift; path = Source/Charts/Data/Implementations/Standard/CandleChartData.swift; sourceTree = ""; }; 7D2323EE20AC1EF48B712A20DBF6494B /* XAxisRendererRadarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XAxisRendererRadarChart.swift; path = Source/Charts/Renderers/XAxisRendererRadarChart.swift; sourceTree = ""; }; 7E8B72865ACD867C65FC699940A86205 /* IShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/IShapeRenderer.swift; sourceTree = ""; }; 7F8378A03C6A74C2F636AE040B39D082 /* Pods-ChartsUnderstandAndUsage.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ChartsUnderstandAndUsage.modulemap"; sourceTree = ""; }; 7FFCF5BE4C67A4A874C3137F15DC58D6 /* DataApproximator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DataApproximator.swift; path = Source/Charts/Filters/DataApproximator.swift; sourceTree = ""; }; 812AD0F04F4695E01AD8F2FC2CAA2718 /* ChartUtils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartUtils.swift; path = Source/Charts/Utils/ChartUtils.swift; sourceTree = ""; }; 82AE4E5B6D9F12829FB2BB33E0AB7C37 /* Highlight.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Highlight.swift; path = Source/Charts/Highlight/Highlight.swift; sourceTree = ""; }; 85A10B71171CCD62F9CCC058D02D5486 /* RadarHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarHighlighter.swift; path = Source/Charts/Highlight/RadarHighlighter.swift; sourceTree = ""; }; 87D30042C23B309BE8FA1105797B3881 /* Transformer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Transformer.swift; path = Source/Charts/Utils/Transformer.swift; sourceTree = ""; }; 88A54F9811E788CA25C899C22FC8CDD9 /* RadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/RadarChartDataSet.swift; sourceTree = ""; }; 88BF0EDD22B86B7053BB600894F8602F /* Legend.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Legend.swift; path = Source/Charts/Components/Legend.swift; sourceTree = ""; }; 8BDEDE32D6063CEA8268F256367DD8A1 /* AxisBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AxisBase.swift; path = Source/Charts/Components/AxisBase.swift; sourceTree = ""; }; 8E36875F175CC38DEC7F65150DD291C5 /* Charts-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Charts-prefix.pch"; sourceTree = ""; }; 8F767B671EEC4586936C5ECFD05EE51D /* Fill.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Fill.swift; path = Source/Charts/Utils/Fill.swift; sourceTree = ""; }; 8F7D18D13C8A9A9FEB7606A1D86E5FC9 /* CandleStickChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleStickChartView.swift; path = Source/Charts/Charts/CandleStickChartView.swift; sourceTree = ""; }; 929872A625629FEAAA6A03279FB743E1 /* BarChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartView.swift; path = Source/Charts/Charts/BarChartView.swift; sourceTree = ""; }; 92A81033F1455249B76D41A872F01834 /* PieRadarHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieRadarHighlighter.swift; path = Source/Charts/Highlight/PieRadarHighlighter.swift; sourceTree = ""; }; 92B446DB72E179C73E723D6663B72331 /* CandleStickChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleStickChartRenderer.swift; path = Source/Charts/Renderers/CandleStickChartRenderer.swift; sourceTree = ""; }; 96ED88D65AF34AED73B86599F51B3715 /* ChevronDownShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChevronDownShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/ChevronDownShapeRenderer.swift; sourceTree = ""; }; 990C40B4FA90E062D44AEE2B861355F6 /* ChevronUpShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChevronUpShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/ChevronUpShapeRenderer.swift; sourceTree = ""; }; 9953CC83D581E416F0DA65D242F29215 /* IChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IChartDataSet.swift; path = Source/Charts/Data/Interfaces/IChartDataSet.swift; sourceTree = ""; }; 9D254093DBD4DA34954D729B1104D048 /* IRadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IRadarChartDataSet.swift; path = Source/Charts/Data/Interfaces/IRadarChartDataSet.swift; sourceTree = ""; }; 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9E721F418A914A0F3EBEC3E53CB14935 /* Animator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Animator.swift; path = Source/Charts/Animation/Animator.swift; sourceTree = ""; }; A014915174EB90A68FDB219160768B6D /* RadarChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarChartView.swift; path = Source/Charts/Charts/RadarChartView.swift; sourceTree = ""; }; A1C1B977ED8804E8AEEC884E7359EE58 /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Charts.framework; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A2544BD484C3F5ABB4EAD6CE6B53130D /* ViewPortHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ViewPortHandler.swift; path = Source/Charts/Utils/ViewPortHandler.swift; sourceTree = ""; }; A2C6184611E77B22F25874CC8B74BE46 /* LineChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/LineChartDataSet.swift; sourceTree = ""; }; A42422E60D433E9E0F99120F92EB6960 /* ScatterChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartDataProvider.swift; path = Source/Charts/Interfaces/ScatterChartDataProvider.swift; sourceTree = ""; }; A4820C006BD5F2D6BC446058D5F26DEB /* ChartViewBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartViewBase.swift; path = Source/Charts/Charts/ChartViewBase.swift; sourceTree = ""; }; A6F19E729FE0680BF24796BE4015725A /* HorizontalBarChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HorizontalBarChartRenderer.swift; path = Source/Charts/Renderers/HorizontalBarChartRenderer.swift; sourceTree = ""; }; A94E9D17D1B8563135F9D35E32168A5A /* LineRadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineRadarChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/LineRadarChartDataSet.swift; sourceTree = ""; }; A953D981FE4D97652B09FEBB96411948 /* IBubbleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IBubbleChartDataSet.swift; path = Source/Charts/Data/Interfaces/IBubbleChartDataSet.swift; sourceTree = ""; }; AA27DD30748D67E97A6CED2F2870AFFA /* HorizontalBarHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HorizontalBarHighlighter.swift; path = Source/Charts/Highlight/HorizontalBarHighlighter.swift; sourceTree = ""; }; AB3B0DA334A74D1E6D3F1A16B78285D9 /* XShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/XShapeRenderer.swift; sourceTree = ""; }; AD494D5B4708D79817062A80D445CFCB /* ICandleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ICandleChartDataSet.swift; path = Source/Charts/Data/Interfaces/ICandleChartDataSet.swift; sourceTree = ""; }; AF95DED3556D8932D4F516D73DB354D8 /* Charts.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Charts.modulemap; sourceTree = ""; }; B0C45599881349FE4360659D131EA8AC /* LineChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartDataProvider.swift; path = Source/Charts/Interfaces/LineChartDataProvider.swift; sourceTree = ""; }; B21FF3D59CCA9526A1259CE00C4F4FD5 /* YAxisRendererRadarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = YAxisRendererRadarChart.swift; path = Source/Charts/Renderers/YAxisRendererRadarChart.swift; sourceTree = ""; }; B2B47CCF8D645E2FBCF5AD3EF0709DDF /* Pods-ChartsUnderstandAndUsage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ChartsUnderstandAndUsage.release.xcconfig"; sourceTree = ""; }; B3B04FC73DEE651FCB3CA890B1887530 /* ChartHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartHighlighter.swift; path = Source/Charts/Highlight/ChartHighlighter.swift; sourceTree = ""; }; B454498F318F86C7DB23A7E85DC47EBE /* IScatterChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IScatterChartDataSet.swift; path = Source/Charts/Data/Interfaces/IScatterChartDataSet.swift; sourceTree = ""; }; B7A6EFA9EFCEF0A9928EA2C44398166C /* ScatterChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartView.swift; path = Source/Charts/Charts/ScatterChartView.swift; sourceTree = ""; }; BA2D5024C31D8A9C9AB00B1A04C4FAFF /* IMarker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IMarker.swift; path = Source/Charts/Components/IMarker.swift; sourceTree = ""; }; BB7893D7FA975910BD8B4C7E91DBC0FF /* CombinedHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CombinedHighlighter.swift; path = Source/Charts/Highlight/CombinedHighlighter.swift; sourceTree = ""; }; BDD0A0893387A7D417C7290B39A7AF86 /* Charts-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Charts-dummy.m"; sourceTree = ""; }; BE145A9E4312F162671A36DAEEB21250 /* SquareShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SquareShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/SquareShapeRenderer.swift; sourceTree = ""; }; C02B97735E54C7C4B8D5C500C701AC6D /* BarHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarHighlighter.swift; path = Source/Charts/Highlight/BarHighlighter.swift; sourceTree = ""; }; C0A7A5CC7697745714306BD2690C79C8 /* LineChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartRenderer.swift; path = Source/Charts/Renderers/LineChartRenderer.swift; sourceTree = ""; }; C10965CFD80A8625FF0880652EA75316 /* CandleChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/CandleChartDataEntry.swift; sourceTree = ""; }; C2DD7F63F80F5E8F924D99314B1A0EC9 /* BarLineChartViewBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarLineChartViewBase.swift; path = Source/Charts/Charts/BarLineChartViewBase.swift; sourceTree = ""; }; C6E3051B875B9C266DCE9E7F900474FD /* Renderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Renderer.swift; path = Source/Charts/Renderers/Renderer.swift; sourceTree = ""; }; C93047F5B2F06C72B285A6A145A3A199 /* IBarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IBarChartDataSet.swift; path = Source/Charts/Data/Interfaces/IBarChartDataSet.swift; sourceTree = ""; }; CB79A7DB9033B56511A7B7B8AE447F82 /* BubbleChartView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartView.swift; path = Source/Charts/Charts/BubbleChartView.swift; sourceTree = ""; }; CC713DA16C24DB30485289E8FFF884B1 /* PieHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieHighlighter.swift; path = Source/Charts/Highlight/PieHighlighter.swift; sourceTree = ""; }; CEC25025089C77D41976265C4F0F58E8 /* ViewPortJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ViewPortJob.swift; path = Source/Charts/Jobs/ViewPortJob.swift; sourceTree = ""; }; D14843F2B92F7FDF1BBD84EA44B74D5C /* Pods-ChartsUnderstandAndUsage-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-ChartsUnderstandAndUsage-frameworks.sh"; sourceTree = ""; }; D2118193BDC7D7F1EAA7D8158425643C /* DefaultAxisValueFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultAxisValueFormatter.swift; path = Source/Charts/Formatters/DefaultAxisValueFormatter.swift; sourceTree = ""; }; D577C0815236E87C6122880D5A9ED9C0 /* XAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = XAxisRendererHorizontalBarChart.swift; path = Source/Charts/Renderers/XAxisRendererHorizontalBarChart.swift; sourceTree = ""; }; D58038678BB787C725CAB1D92B973C65 /* ChartDataRendererBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartDataRendererBase.swift; path = Source/Charts/Renderers/ChartDataRendererBase.swift; sourceTree = ""; }; D9997A233D1B97B14EC5DD2E669F98A6 /* ScatterChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartData.swift; path = Source/Charts/Data/Implementations/Standard/ScatterChartData.swift; sourceTree = ""; }; DB224F2962F270CFFF70379E5E71B373 /* DefaultValueFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultValueFormatter.swift; path = Source/Charts/Formatters/DefaultValueFormatter.swift; sourceTree = ""; }; DE04063011E2AA67BCB09C07D709F201 /* YAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = YAxisRendererHorizontalBarChart.swift; path = Source/Charts/Renderers/YAxisRendererHorizontalBarChart.swift; sourceTree = ""; }; DEA77C52B1FD92FF2B32F140E02FAB95 /* BarChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartData.swift; path = Source/Charts/Data/Implementations/Standard/BarChartData.swift; sourceTree = ""; }; E1B6524729FA9C069B58A8A2A01E166B /* Charts.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Charts.debug.xcconfig; sourceTree = ""; }; E1E316EAD5FAC4F627B8C539B9E818BB /* ILineScatterCandleRadarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ILineScatterCandleRadarChartDataSet.swift; path = Source/Charts/Data/Interfaces/ILineScatterCandleRadarChartDataSet.swift; sourceTree = ""; }; E2B629D8CD1EDD2268CF1DFCDAE865B0 /* BarLineScatterCandleBubbleRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarLineScatterCandleBubbleRenderer.swift; path = Source/Charts/Renderers/BarLineScatterCandleBubbleRenderer.swift; sourceTree = ""; }; E727EB214E6927C9256929A069BDDA84 /* BubbleChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartRenderer.swift; path = Source/Charts/Renderers/BubbleChartRenderer.swift; sourceTree = ""; }; E790CEAF83BFB05C9EBC292A499B8E21 /* PieChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieChartRenderer.swift; path = Source/Charts/Renderers/PieChartRenderer.swift; sourceTree = ""; }; E8028C080C73771E040542C877894F98 /* CandleChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CandleChartDataProvider.swift; path = Source/Charts/Interfaces/CandleChartDataProvider.swift; sourceTree = ""; }; E80917B53E0AE415F6EAD4B11B543E7C /* DefaultFillFormatter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultFillFormatter.swift; path = Source/Charts/Formatters/DefaultFillFormatter.swift; sourceTree = ""; }; E87871D0FE0AA14E98C678F0C8D38738 /* PieRadarChartViewBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieRadarChartViewBase.swift; path = Source/Charts/Charts/PieRadarChartViewBase.swift; sourceTree = ""; }; E88B24545A64CA0048CC9EBA7C5BBE4D /* PieChartDataEntry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PieChartDataEntry.swift; path = Source/Charts/Data/Implementations/Standard/PieChartDataEntry.swift; sourceTree = ""; }; E9C20F8DF45C00A25B755DD2FC48DC27 /* Pods-ChartsUnderstandAndUsage-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ChartsUnderstandAndUsage-acknowledgements.markdown"; sourceTree = ""; }; EB48BA456A0A69C1DC19A530843B6A9F /* BubbleChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BubbleChartDataProvider.swift; path = Source/Charts/Interfaces/BubbleChartDataProvider.swift; sourceTree = ""; }; ECDEDA2CD5334E58AF14669B92154AFB /* BarChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/BarChartDataSet.swift; sourceTree = ""; }; ED02C3CCA3B55AE99E9DC95CF505D036 /* ChartBaseDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartBaseDataSet.swift; path = Source/Charts/Data/Implementations/ChartBaseDataSet.swift; sourceTree = ""; }; ED235828F622CA8CB815A6FF1233C580 /* ChartColorTemplates.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartColorTemplates.swift; path = Source/Charts/Utils/ChartColorTemplates.swift; sourceTree = ""; }; ED40BB10FA5C856533DC26901848A511 /* Pods-ChartsUnderstandAndUsage-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ChartsUnderstandAndUsage-Info.plist"; sourceTree = ""; }; EE0E7AE74D531F333CB8FC4A9DA1616F /* IHighlighter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IHighlighter.swift; path = Source/Charts/Highlight/IHighlighter.swift; sourceTree = ""; }; EEBA64CD2F1E7C2BB8CDCFDC9DA28BAF /* Pods-ChartsUnderstandAndUsage-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ChartsUnderstandAndUsage-dummy.m"; sourceTree = ""; }; F10E9673F9B77BAB3587661DA9FA16DE /* LineRadarRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineRadarRenderer.swift; path = Source/Charts/Renderers/LineRadarRenderer.swift; sourceTree = ""; }; F1AB697393A4A48A88649EA3E5BB49CF /* Description.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Description.swift; path = Source/Charts/Components/Description.swift; sourceTree = ""; }; F2E56156DA5E1404A1E9D6C20E729FCB /* YAxis.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = YAxis.swift; path = Source/Charts/Components/YAxis.swift; sourceTree = ""; }; F2F7305A3B93E6041536BB02BB80B480 /* AnimatedZoomViewJob.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedZoomViewJob.swift; path = Source/Charts/Jobs/AnimatedZoomViewJob.swift; sourceTree = ""; }; F340AC726041C97BCA3EEF9607250BEC /* ChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartData.swift; path = Source/Charts/Data/Implementations/Standard/ChartData.swift; sourceTree = ""; }; F46427C7A4AA6AD8C4D3519E28915663 /* LineChartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartData.swift; path = Source/Charts/Data/Implementations/Standard/LineChartData.swift; sourceTree = ""; }; F5BF0B4B02C0E09EC7383F4F4A80F2C7 /* Pods-ChartsUnderstandAndUsage-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ChartsUnderstandAndUsage-umbrella.h"; sourceTree = ""; }; F6216B4437A19C60DD91947242EF95DB /* RadarChartRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RadarChartRenderer.swift; path = Source/Charts/Renderers/RadarChartRenderer.swift; sourceTree = ""; }; F7CC9A2A4F739DA1AE4752907BFDF35D /* BarChartDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarChartDataProvider.swift; path = Source/Charts/Interfaces/BarChartDataProvider.swift; sourceTree = ""; }; FAF7777FE62D3C1EB5630B94E0C156C2 /* MarkerView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MarkerView.swift; path = Source/Charts/Components/MarkerView.swift; sourceTree = ""; }; FEAEAEBADF1D0FACFB05A283ABD9F289 /* BarLineScatterCandleBubbleChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BarLineScatterCandleBubbleChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/BarLineScatterCandleBubbleChartDataSet.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 348A68EFD37E77A1DC8A233E95ED5724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 2E366CD79E7A871563B427DDF15FA676 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 7C082FE647A1AE2BCE297BA76A33C6D7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 3833B92370E686B9CEED0755059A1C29 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 165626B75592795E4943A4253927EDD5 /* Support Files */ = { isa = PBXGroup; children = ( AF95DED3556D8932D4F516D73DB354D8 /* Charts.modulemap */, BDD0A0893387A7D417C7290B39A7AF86 /* Charts-dummy.m */, 8E36875F175CC38DEC7F65150DD291C5 /* Charts-prefix.pch */, 67C4A2A1421AE830E9071E84133869BA /* Charts-umbrella.h */, E1B6524729FA9C069B58A8A2A01E166B /* Charts.debug.xcconfig */, 0394FC7CC3C3816823F09785CDB203C6 /* Charts.release.xcconfig */, ); name = "Support Files"; path = "../Target Support Files/Charts"; sourceTree = ""; }; 563DE5651F597D59B7B4525159FD9490 /* Products */ = { isa = PBXGroup; children = ( A1C1B977ED8804E8AEEC884E7359EE58 /* Charts.framework */, 5144D14E205EDED26DC3F8DC591EB3A2 /* Pods_ChartsUnderstandAndUsage.framework */, ); name = Products; sourceTree = ""; }; 578452D2E740E91742655AC8F1636D1F /* iOS */ = { isa = PBXGroup; children = ( 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, ); name = iOS; sourceTree = ""; }; BB93D88C7CB0D972DEAC74DA4F863096 /* Targets Support Files */ = { isa = PBXGroup; children = ( CE99E7C6C08A8D34FB3E8DB8DE0538F6 /* Pods-ChartsUnderstandAndUsage */, ); name = "Targets Support Files"; sourceTree = ""; }; C0DF6D273683739462CD8D21F09EBBA6 /* Charts */ = { isa = PBXGroup; children = ( C390B4C86CDA484BA0FBB4297101B0D0 /* Core */, 165626B75592795E4943A4253927EDD5 /* Support Files */, ); name = Charts; path = Charts; sourceTree = ""; }; C390B4C86CDA484BA0FBB4297101B0D0 /* Core */ = { isa = PBXGroup; children = ( 27B0DE3264F632D6FE8BDE7528540564 /* AnimatedMoveViewJob.swift */, 5D55BAD3DB826448025A7E587A7B7CC8 /* AnimatedViewPortJob.swift */, F2F7305A3B93E6041536BB02BB80B480 /* AnimatedZoomViewJob.swift */, 9E721F418A914A0F3EBEC3E53CB14935 /* Animator.swift */, 8BDEDE32D6063CEA8268F256367DD8A1 /* AxisBase.swift */, 0FC357067FDF91B681F15DC4BDA24274 /* AxisRendererBase.swift */, DEA77C52B1FD92FF2B32F140E02FAB95 /* BarChartData.swift */, 71405DE5EBB0DAD4D8C3E6F37A38F887 /* BarChartDataEntry.swift */, F7CC9A2A4F739DA1AE4752907BFDF35D /* BarChartDataProvider.swift */, ECDEDA2CD5334E58AF14669B92154AFB /* BarChartDataSet.swift */, 00CB662CBF72CFD63348D2BB4AB2E985 /* BarChartRenderer.swift */, 929872A625629FEAAA6A03279FB743E1 /* BarChartView.swift */, C02B97735E54C7C4B8D5C500C701AC6D /* BarHighlighter.swift */, C2DD7F63F80F5E8F924D99314B1A0EC9 /* BarLineChartViewBase.swift */, 258EDF70931EB74813A2DD320306BA33 /* BarLineScatterCandleBubbleChartData.swift */, 388A603D6F6A5951453D13EFDFBFFF3D /* BarLineScatterCandleBubbleChartDataProvider.swift */, FEAEAEBADF1D0FACFB05A283ABD9F289 /* BarLineScatterCandleBubbleChartDataSet.swift */, E2B629D8CD1EDD2268CF1DFCDAE865B0 /* BarLineScatterCandleBubbleRenderer.swift */, 3D6DCA547E4BA53F4B3C052C622EC13D /* BubbleChartData.swift */, 453494AC5B704315FD7793D0D394F663 /* BubbleChartDataEntry.swift */, EB48BA456A0A69C1DC19A530843B6A9F /* BubbleChartDataProvider.swift */, 3D4A94014E26341A7B22762740ACA5FE /* BubbleChartDataSet.swift */, E727EB214E6927C9256929A069BDDA84 /* BubbleChartRenderer.swift */, CB79A7DB9033B56511A7B7B8AE447F82 /* BubbleChartView.swift */, 7C64BD2349360E0B3C3CA75B0BD3894E /* CandleChartData.swift */, C10965CFD80A8625FF0880652EA75316 /* CandleChartDataEntry.swift */, E8028C080C73771E040542C877894F98 /* CandleChartDataProvider.swift */, 51E64E474C4D9C0766155F26F7D296E9 /* CandleChartDataSet.swift */, 92B446DB72E179C73E723D6663B72331 /* CandleStickChartRenderer.swift */, 8F7D18D13C8A9A9FEB7606A1D86E5FC9 /* CandleStickChartView.swift */, 29DB4C76953164FFEE710830C4831670 /* ChartAnimationEasing.swift */, ED02C3CCA3B55AE99E9DC95CF505D036 /* ChartBaseDataSet.swift */, ED235828F622CA8CB815A6FF1233C580 /* ChartColorTemplates.swift */, F340AC726041C97BCA3EEF9607250BEC /* ChartData.swift */, 19BB904A3F49CD6633D8578419F2C44E /* ChartDataEntry.swift */, 598CD5E177243E5C0F91B307CB95AE84 /* ChartDataEntryBase.swift */, 494AFC909175F5F2B1FA37AEE828D5E1 /* ChartDataProvider.swift */, D58038678BB787C725CAB1D92B973C65 /* ChartDataRendererBase.swift */, 38D649DF87AB0C9EF8A7D4131698A2EA /* ChartDataSet.swift */, B3B04FC73DEE651FCB3CA890B1887530 /* ChartHighlighter.swift */, 7AA781C0D80DD82A580DEB8A1255DD74 /* ChartLimitLine.swift */, 812AD0F04F4695E01AD8F2FC2CAA2718 /* ChartUtils.swift */, A4820C006BD5F2D6BC446058D5F26DEB /* ChartViewBase.swift */, 96ED88D65AF34AED73B86599F51B3715 /* ChevronDownShapeRenderer.swift */, 990C40B4FA90E062D44AEE2B861355F6 /* ChevronUpShapeRenderer.swift */, 5DD30AB1BFD8A3AEA97BA9D336D944B5 /* CircleShapeRenderer.swift */, 4A70BCDA190E40E078ECCB211F647DDE /* CombinedChartData.swift */, 1A3A6B5F94C303A2379A82A4450D2866 /* CombinedChartDataProvider.swift */, 15027A836EB4E05B5BF6AF01E1C18561 /* CombinedChartRenderer.swift */, 28E90F74B7709CECB701B57153C9B90B /* CombinedChartView.swift */, BB7893D7FA975910BD8B4C7E91DBC0FF /* CombinedHighlighter.swift */, 209A5D2750113D9169AC70DE5A4FBB77 /* ComponentBase.swift */, 0427EF510D6604A39DEB0E64750BDB30 /* CrossShapeRenderer.swift */, 7FFCF5BE4C67A4A874C3137F15DC58D6 /* DataApproximator.swift */, 786A9572EE474CD5AD3AA027402F7B3C /* DataApproximator+N.swift */, D2118193BDC7D7F1EAA7D8158425643C /* DefaultAxisValueFormatter.swift */, E80917B53E0AE415F6EAD4B11B543E7C /* DefaultFillFormatter.swift */, DB224F2962F270CFFF70379E5E71B373 /* DefaultValueFormatter.swift */, F1AB697393A4A48A88649EA3E5BB49CF /* Description.swift */, 8F767B671EEC4586936C5ECFD05EE51D /* Fill.swift */, 82AE4E5B6D9F12829FB2BB33E0AB7C37 /* Highlight.swift */, A6F19E729FE0680BF24796BE4015725A /* HorizontalBarChartRenderer.swift */, 40F6967D5284C16C29D9616E9082AF73 /* HorizontalBarChartView.swift */, AA27DD30748D67E97A6CED2F2870AFFA /* HorizontalBarHighlighter.swift */, 57AEC775324B52FE2C100D64BEB39B31 /* IAxisValueFormatter.swift */, C93047F5B2F06C72B285A6A145A3A199 /* IBarChartDataSet.swift */, 007ACE796435033B39D972A7CA33ABEB /* IBarLineScatterCandleBubbleChartDataSet.swift */, A953D981FE4D97652B09FEBB96411948 /* IBubbleChartDataSet.swift */, AD494D5B4708D79817062A80D445CFCB /* ICandleChartDataSet.swift */, 9953CC83D581E416F0DA65D242F29215 /* IChartDataSet.swift */, 525492DBE52C9C8B11C212DB8A6D7469 /* IFillFormatter.swift */, EE0E7AE74D531F333CB8FC4A9DA1616F /* IHighlighter.swift */, 5EADD8733404CC75894D869C9048EAEA /* ILineChartDataSet.swift */, 47B19DFF38B1079F518E3F07E75BC3C6 /* ILineRadarChartDataSet.swift */, E1E316EAD5FAC4F627B8C539B9E818BB /* ILineScatterCandleRadarChartDataSet.swift */, BA2D5024C31D8A9C9AB00B1A04C4FAFF /* IMarker.swift */, 1D98175BB36E4F7BEF08576940853429 /* IndexAxisValueFormatter.swift */, 0F59E68FA6685EBB93531B0F6D1AD184 /* IPieChartDataSet.swift */, 9D254093DBD4DA34954D729B1104D048 /* IRadarChartDataSet.swift */, B454498F318F86C7DB23A7E85DC47EBE /* IScatterChartDataSet.swift */, 7E8B72865ACD867C65FC699940A86205 /* IShapeRenderer.swift */, 2645E6CA6C5CFE4378102FE21D376B85 /* IValueFormatter.swift */, 88BF0EDD22B86B7053BB600894F8602F /* Legend.swift */, 08FCB5ADD20BE774EC67978F8CFA2BFB /* LegendEntry.swift */, 37A0BC2B5325B768A61A9B412A7C5CCA /* LegendRenderer.swift */, F46427C7A4AA6AD8C4D3519E28915663 /* LineChartData.swift */, B0C45599881349FE4360659D131EA8AC /* LineChartDataProvider.swift */, A2C6184611E77B22F25874CC8B74BE46 /* LineChartDataSet.swift */, C0A7A5CC7697745714306BD2690C79C8 /* LineChartRenderer.swift */, 44D9EB04F3E3D05CFAF943DD7C584D35 /* LineChartView.swift */, A94E9D17D1B8563135F9D35E32168A5A /* LineRadarChartDataSet.swift */, F10E9673F9B77BAB3587661DA9FA16DE /* LineRadarRenderer.swift */, 2B2EDF10F582435C12DCB45001619373 /* LineScatterCandleRadarChartDataSet.swift */, 78FCA6EF2D206D8FAD19D32068367859 /* LineScatterCandleRadarRenderer.swift */, 4CFEF8702759A1E45A5306231BDD8533 /* MarkerImage.swift */, FAF7777FE62D3C1EB5630B94E0C156C2 /* MarkerView.swift */, 3D664CB09A2C251499554D5B63A0B442 /* MoveViewJob.swift */, 63A1FC4B24CEC527E730D3ED4BD08AC7 /* PieChartData.swift */, E88B24545A64CA0048CC9EBA7C5BBE4D /* PieChartDataEntry.swift */, 008321D48AF3972C2E4DFAA4F8D01E81 /* PieChartDataSet.swift */, E790CEAF83BFB05C9EBC292A499B8E21 /* PieChartRenderer.swift */, 3994AB0FE4D3C98A5DC0F47EC1D36E86 /* PieChartView.swift */, CC713DA16C24DB30485289E8FFF884B1 /* PieHighlighter.swift */, E87871D0FE0AA14E98C678F0C8D38738 /* PieRadarChartViewBase.swift */, 92A81033F1455249B76D41A872F01834 /* PieRadarHighlighter.swift */, 165DBA3CCC4338298A6DF2DE448F507A /* Platform.swift */, 29AE450AA18DB25C104B10D00CCBF997 /* RadarChartData.swift */, 4626EF42600E567686581FB4198E4DEC /* RadarChartDataEntry.swift */, 88A54F9811E788CA25C899C22FC8CDD9 /* RadarChartDataSet.swift */, F6216B4437A19C60DD91947242EF95DB /* RadarChartRenderer.swift */, A014915174EB90A68FDB219160768B6D /* RadarChartView.swift */, 85A10B71171CCD62F9CCC058D02D5486 /* RadarHighlighter.swift */, 113774AEFE9BEF3248173AD2D14C9494 /* Range.swift */, C6E3051B875B9C266DCE9E7F900474FD /* Renderer.swift */, D9997A233D1B97B14EC5DD2E669F98A6 /* ScatterChartData.swift */, A42422E60D433E9E0F99120F92EB6960 /* ScatterChartDataProvider.swift */, 484C682104D106ED4EEA9595034B0B5E /* ScatterChartDataSet.swift */, 0CD8827780795878F316BFE265FA3A4E /* ScatterChartRenderer.swift */, B7A6EFA9EFCEF0A9928EA2C44398166C /* ScatterChartView.swift */, BE145A9E4312F162671A36DAEEB21250 /* SquareShapeRenderer.swift */, 87D30042C23B309BE8FA1105797B3881 /* Transformer.swift */, 0FEBA9D8B13E52514910FC991D0DFC25 /* TransformerHorizontalBarChart.swift */, 33EE377C802C9F4F19B0AC65814351DD /* TriangleShapeRenderer.swift */, A2544BD484C3F5ABB4EAD6CE6B53130D /* ViewPortHandler.swift */, CEC25025089C77D41976265C4F0F58E8 /* ViewPortJob.swift */, 657F9EE6FC7DE799B1718E62EA2C853E /* XAxis.swift */, 7019B7E6DB7EDB6E22A0CF9AC96BAD2F /* XAxisRenderer.swift */, D577C0815236E87C6122880D5A9ED9C0 /* XAxisRendererHorizontalBarChart.swift */, 7D2323EE20AC1EF48B712A20DBF6494B /* XAxisRendererRadarChart.swift */, AB3B0DA334A74D1E6D3F1A16B78285D9 /* XShapeRenderer.swift */, F2E56156DA5E1404A1E9D6C20E729FCB /* YAxis.swift */, 665E8BD2EA3EF1FC2604151E27ED6514 /* YAxisRenderer.swift */, DE04063011E2AA67BCB09C07D709F201 /* YAxisRendererHorizontalBarChart.swift */, B21FF3D59CCA9526A1259CE00C4F4FD5 /* YAxisRendererRadarChart.swift */, 47E21D64E16B230057560FD9B171417D /* ZoomViewJob.swift */, ); name = Core; sourceTree = ""; }; CE99E7C6C08A8D34FB3E8DB8DE0538F6 /* Pods-ChartsUnderstandAndUsage */ = { isa = PBXGroup; children = ( 7F8378A03C6A74C2F636AE040B39D082 /* Pods-ChartsUnderstandAndUsage.modulemap */, E9C20F8DF45C00A25B755DD2FC48DC27 /* Pods-ChartsUnderstandAndUsage-acknowledgements.markdown */, 607EF751D5BAB9BCA337430FDC584C14 /* Pods-ChartsUnderstandAndUsage-acknowledgements.plist */, EEBA64CD2F1E7C2BB8CDCFDC9DA28BAF /* Pods-ChartsUnderstandAndUsage-dummy.m */, D14843F2B92F7FDF1BBD84EA44B74D5C /* Pods-ChartsUnderstandAndUsage-frameworks.sh */, ED40BB10FA5C856533DC26901848A511 /* Pods-ChartsUnderstandAndUsage-Info.plist */, F5BF0B4B02C0E09EC7383F4F4A80F2C7 /* Pods-ChartsUnderstandAndUsage-umbrella.h */, 6F6C51CB6D62B862D7993889D948085C /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */, B2B47CCF8D645E2FBCF5AD3EF0709DDF /* Pods-ChartsUnderstandAndUsage.release.xcconfig */, ); name = "Pods-ChartsUnderstandAndUsage"; path = "Target Support Files/Pods-ChartsUnderstandAndUsage"; sourceTree = ""; }; CF1408CF629C7361332E53B88F7BD30C = { isa = PBXGroup; children = ( 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, E5AB24ECFD8DBB88747DFD7EDF5BF05C /* Pods */, 563DE5651F597D59B7B4525159FD9490 /* Products */, BB93D88C7CB0D972DEAC74DA4F863096 /* Targets Support Files */, ); sourceTree = ""; }; D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { isa = PBXGroup; children = ( 578452D2E740E91742655AC8F1636D1F /* iOS */, ); name = Frameworks; sourceTree = ""; }; E5AB24ECFD8DBB88747DFD7EDF5BF05C /* Pods */ = { isa = PBXGroup; children = ( C0DF6D273683739462CD8D21F09EBBA6 /* Charts */, ); name = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ A8053E5A06AC225CFE3D93408319C38B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 3B5EFB86415AC3FEDF5D9A7D02DE62F9 /* Charts-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; C65D01B10F3EC110A8BCCBD1C8CCECC1 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 724C6942E911FAD8F02E2049C06CEF0C /* Pods-ChartsUnderstandAndUsage-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 83D0653CA09C7E984E28FD2C035B4862 /* Pods-ChartsUnderstandAndUsage */ = { isa = PBXNativeTarget; buildConfigurationList = D6F23D399EE007FB2413D8F16EEF6AAC /* Build configuration list for PBXNativeTarget "Pods-ChartsUnderstandAndUsage" */; buildPhases = ( C65D01B10F3EC110A8BCCBD1C8CCECC1 /* Headers */, 6E8440064A8E450872C8E6B1824FDD84 /* Sources */, 348A68EFD37E77A1DC8A233E95ED5724 /* Frameworks */, 9472C076304E1B996C16471948EB6A36 /* Resources */, ); buildRules = ( ); dependencies = ( 29932759013A9009BE143BE54DB904B1 /* PBXTargetDependency */, ); name = "Pods-ChartsUnderstandAndUsage"; productName = "Pods-ChartsUnderstandAndUsage"; productReference = 5144D14E205EDED26DC3F8DC591EB3A2 /* Pods_ChartsUnderstandAndUsage.framework */; productType = "com.apple.product-type.framework"; }; BF03B0B2B8B052092CB5F90CC5FB3757 /* Charts */ = { isa = PBXNativeTarget; buildConfigurationList = D41945FCBF56EF7C8C86ADDA907EE076 /* Build configuration list for PBXNativeTarget "Charts" */; buildPhases = ( A8053E5A06AC225CFE3D93408319C38B /* Headers */, 2318A0FB8E557683ED38112AFF287B3F /* Sources */, 7C082FE647A1AE2BCE297BA76A33C6D7 /* Frameworks */, 5B1EFE357DE587E41EA0FC7BE53F4FB9 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Charts; productName = Charts; productReference = A1C1B977ED8804E8AEEC884E7359EE58 /* Charts.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ BFDFE7DC352907FC980B868725387E98 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1240; LastUpgradeCheck = 1240; }; buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 10.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = CF1408CF629C7361332E53B88F7BD30C; productRefGroup = 563DE5651F597D59B7B4525159FD9490 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( BF03B0B2B8B052092CB5F90CC5FB3757 /* Charts */, 83D0653CA09C7E984E28FD2C035B4862 /* Pods-ChartsUnderstandAndUsage */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 5B1EFE357DE587E41EA0FC7BE53F4FB9 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 9472C076304E1B996C16471948EB6A36 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 2318A0FB8E557683ED38112AFF287B3F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( B0A6783A037B5D31DB686EE4E9B31F0E /* AnimatedMoveViewJob.swift in Sources */, B0282A8E32E1E1448A731E2DE9B4F16C /* AnimatedViewPortJob.swift in Sources */, 678061020C1872E749B279665E11DB43 /* AnimatedZoomViewJob.swift in Sources */, CCB2247822E46CE23C53D36F72C807E9 /* Animator.swift in Sources */, 0B9D63094D8353F890EC4D3A18A3B8D2 /* AxisBase.swift in Sources */, 1F5DCE18354B48B39D86445A3D0CACDE /* AxisRendererBase.swift in Sources */, 4E3A1CC25A3E9136B8AA354099970541 /* BarChartData.swift in Sources */, 0CC0331802115AEFB065F553BEFC26CA /* BarChartDataEntry.swift in Sources */, 8AF733E74DCD1B2573B69ADDA1A27714 /* BarChartDataProvider.swift in Sources */, FEA1B96EEE907DCE04685151022AD6B0 /* BarChartDataSet.swift in Sources */, 73D7B5895D94797825C3F74FF95E2BE1 /* BarChartRenderer.swift in Sources */, F2DAF14754E7BCAD4BEA1E4BE5F46C40 /* BarChartView.swift in Sources */, 30AB361CB3D892588576FEC790654E12 /* BarHighlighter.swift in Sources */, 084B6340D45ABD3F507E18C9BADF6C20 /* BarLineChartViewBase.swift in Sources */, D927DDE6750131B8B5BFD4CD1473C1F7 /* BarLineScatterCandleBubbleChartData.swift in Sources */, D90C8B456D4DEA6A52CF1CE4C22214B0 /* BarLineScatterCandleBubbleChartDataProvider.swift in Sources */, 9CE18CD1A6115AF2CBAFA8A7680CF2EF /* BarLineScatterCandleBubbleChartDataSet.swift in Sources */, CCEC0460147ED3D1C065D34AFAAE1983 /* BarLineScatterCandleBubbleRenderer.swift in Sources */, E83897772D05EBC9225C8944F42F169B /* BubbleChartData.swift in Sources */, 635B44240C7EA306309A9112E2884214 /* BubbleChartDataEntry.swift in Sources */, 12F85AFD15CD608E23416A9310E97B47 /* BubbleChartDataProvider.swift in Sources */, 9258C60F7F2E419A812E54116650E228 /* BubbleChartDataSet.swift in Sources */, C0262EC2FBB976BA224509F09A161E8B /* BubbleChartRenderer.swift in Sources */, 9C525A5B546DB356A60BC24C5FD2ADDA /* BubbleChartView.swift in Sources */, 88F85B9CE41D86B8356B0958BF303897 /* CandleChartData.swift in Sources */, 699AD84F8DCE24C54A47FD3E6AC2C363 /* CandleChartDataEntry.swift in Sources */, A6D06811E3E52D1A586FCEE1BB8E7CF9 /* CandleChartDataProvider.swift in Sources */, 45C99C760AB49ED28A4DD956AD29953B /* CandleChartDataSet.swift in Sources */, 0A3BC570D93DCC4F28B982C8C17E63EF /* CandleStickChartRenderer.swift in Sources */, 9B40DB5E7B2FF344B275F9666A4B7188 /* CandleStickChartView.swift in Sources */, 34BAA315419AB4D8EE2719368B03854A /* ChartAnimationEasing.swift in Sources */, 6066C3E705478F5CFE95FEB766FA3BDC /* ChartBaseDataSet.swift in Sources */, 1F685D6C872C659CFC6C69113281577C /* ChartColorTemplates.swift in Sources */, C07BCF3B9148133703F40DDEC2AC0916 /* ChartData.swift in Sources */, 400BF641FF5A5D6D658CFEF486539C8E /* ChartDataEntry.swift in Sources */, 550BB7099D4BF13B788AF99C5CF7DBB2 /* ChartDataEntryBase.swift in Sources */, 5D06EECF467542A799500755ACCFE6B2 /* ChartDataProvider.swift in Sources */, 559AE6BC5D7AFAF699C1B689474F5FA0 /* ChartDataRendererBase.swift in Sources */, FC68B5BE8559E7CEAE321C78AC267D3D /* ChartDataSet.swift in Sources */, 522608D640101E73C800C9DE6EF8FB45 /* ChartHighlighter.swift in Sources */, D647539D105E5808D02B2F7B831AFF03 /* ChartLimitLine.swift in Sources */, 662ABB1F60EFAF4C8FC7063DE4CB8B16 /* Charts-dummy.m in Sources */, 9567217C2831B5AA047F5E05C2343E44 /* ChartUtils.swift in Sources */, B622420C87D8B10622F0251F1DB43539 /* ChartViewBase.swift in Sources */, 76C01F136C752E2ACAD315A7176EDFF2 /* ChevronDownShapeRenderer.swift in Sources */, F6C7C354E96D0D0AFCE1A1A952EF946F /* ChevronUpShapeRenderer.swift in Sources */, 483DA7418A3DD9D979793424ACA415A8 /* CircleShapeRenderer.swift in Sources */, 6BD08D93E6B3EFA0271413456E10419E /* CombinedChartData.swift in Sources */, DDB6AB1A86E9233A63F845939F40FE88 /* CombinedChartDataProvider.swift in Sources */, 599767D881AEB44D6C83BFFD56A87E58 /* CombinedChartRenderer.swift in Sources */, DE224D37757460451813B12885B9A08C /* CombinedChartView.swift in Sources */, C69BFCE003CD359A9A19D06947B396C6 /* CombinedHighlighter.swift in Sources */, FED28187B78D99CC0AFE568A2DEE625B /* ComponentBase.swift in Sources */, D1E91C022180D9035B715F1FCFE92801 /* CrossShapeRenderer.swift in Sources */, 37F1A09C1DFE65A585B6F7E1067A738F /* DataApproximator.swift in Sources */, 3F6937EECDD01987607446AF2CBB1A9E /* DataApproximator+N.swift in Sources */, E126E9E22A04ACBA264DAE51CC186F2F /* DefaultAxisValueFormatter.swift in Sources */, E77FDA7A004007FDD84B3C1DE2AB851C /* DefaultFillFormatter.swift in Sources */, E9A302DEB341C0585984FFB722F86508 /* DefaultValueFormatter.swift in Sources */, 4382960229B403F2A50C4AD1AC9D7A5A /* Description.swift in Sources */, 0F89F1E27F45279300296F9BD900C487 /* Fill.swift in Sources */, 4917571E1362E48CAD513F065882CE49 /* Highlight.swift in Sources */, 50418844F42352CED365ADD7E0270AC4 /* HorizontalBarChartRenderer.swift in Sources */, F98383377531639D2890A7588F148DAF /* HorizontalBarChartView.swift in Sources */, 7415DBDD6B7746AC4F20CE7834501B04 /* HorizontalBarHighlighter.swift in Sources */, 13C5C749E0027B88BB7FF0FCC7356226 /* IAxisValueFormatter.swift in Sources */, B4F6F63AFBA13608371C01D774BF8624 /* IBarChartDataSet.swift in Sources */, 0046C1AB90F4417C15D95D7E270E86F6 /* IBarLineScatterCandleBubbleChartDataSet.swift in Sources */, 6AFA62D77EA662A8B783850FE2F1FEA2 /* IBubbleChartDataSet.swift in Sources */, D3AFA3E484882E0AF08FA2EB2B7E4D22 /* ICandleChartDataSet.swift in Sources */, DB78D0054C513B970A441A329A6C85CF /* IChartDataSet.swift in Sources */, 1A90212E5C0883623E8C9EEFC5D61933 /* IFillFormatter.swift in Sources */, 93E80ACA163EE24045B4851AE570BB1F /* IHighlighter.swift in Sources */, D7AA6CF3FFC9783346A2899FF8100161 /* ILineChartDataSet.swift in Sources */, 709F5F7B52E7F4F7E0B9E8F8B0D06D14 /* ILineRadarChartDataSet.swift in Sources */, 24EB442B6A20FE35818736630ED27035 /* ILineScatterCandleRadarChartDataSet.swift in Sources */, 61A60DC3211280217C1FB1CE311D357B /* IMarker.swift in Sources */, 6A5F4AB33903B7A22D7504B0A368E871 /* IndexAxisValueFormatter.swift in Sources */, F087F143628157125CD0B5572AE784AD /* IPieChartDataSet.swift in Sources */, 55377CD1DE87856B328CE723D7723BEC /* IRadarChartDataSet.swift in Sources */, DD2F99BB6E7F20B2CCF97A72B512C8E6 /* IScatterChartDataSet.swift in Sources */, 625AF0CE4C4446338C9EB59A815ABDC9 /* IShapeRenderer.swift in Sources */, E5DFB46E97D8872DC7FC1B99D796FDCE /* IValueFormatter.swift in Sources */, 11C9A467C40E4AB6E6C40394EDB6092F /* Legend.swift in Sources */, 7ACC2B5EA9D1AE5F69EAA9EBF83A67BD /* LegendEntry.swift in Sources */, 3C039B985E79F6F849BD19D66B6C60A5 /* LegendRenderer.swift in Sources */, 68622CB4D5CFD97D4F097024476451BD /* LineChartData.swift in Sources */, D8708EC587000DA1C24D7CB760B5829D /* LineChartDataProvider.swift in Sources */, E12E7BA51F8E7FC3649AB6F644313036 /* LineChartDataSet.swift in Sources */, 513BEDD62A390D06A6E94D4FD8B0F302 /* LineChartRenderer.swift in Sources */, 76BAF63CC685FC732F6EBD277050D62A /* LineChartView.swift in Sources */, 0B55EB9B68BAB7A03A295CB522261BAF /* LineRadarChartDataSet.swift in Sources */, 4FB99CA582685A45E97AF7B3AF7933B1 /* LineRadarRenderer.swift in Sources */, 539FD377F21414A041563D5D36F9B88C /* LineScatterCandleRadarChartDataSet.swift in Sources */, 2D7B833632CB3920E0068A7C1F25FE38 /* LineScatterCandleRadarRenderer.swift in Sources */, 79A2AAACDF64DE2DB862910BCEE6F1CC /* MarkerImage.swift in Sources */, 4A98B4A980615E01C1432CDDDF029118 /* MarkerView.swift in Sources */, 842046A543F7B928A7A2F1673AD50EDE /* MoveViewJob.swift in Sources */, B786863F4221D6DB4F04C7AF61CC70DA /* PieChartData.swift in Sources */, BAA7516B212056F1244ED08788690AEB /* PieChartDataEntry.swift in Sources */, 57A0E17849CB58C8AEFCF6ED0EFD6E45 /* PieChartDataSet.swift in Sources */, 2CF0DD46EBD523F09A7E9F77A8671FE3 /* PieChartRenderer.swift in Sources */, FD3792C290ADDE889980FAE5E3B3D45E /* PieChartView.swift in Sources */, 29EA31025E6D83C37E0BA4B06A222CB4 /* PieHighlighter.swift in Sources */, 8E50FEF8873FA6756E05749D46DD623E /* PieRadarChartViewBase.swift in Sources */, 90E4A90C5A172097D1DF30DABB7FC67C /* PieRadarHighlighter.swift in Sources */, 2F958D66B40343652DC2A08CB3236C9A /* Platform.swift in Sources */, 616234121EDE60031BA2CA550BF3429E /* RadarChartData.swift in Sources */, 18069F821440816E7977B79645A1A304 /* RadarChartDataEntry.swift in Sources */, AC045E2F3D22FE57B16A071657902A1D /* RadarChartDataSet.swift in Sources */, 1537742A59DB4D25428098AACF5F625C /* RadarChartRenderer.swift in Sources */, FD68C7F7AAA4C11EDFBCE1EB61AF3978 /* RadarChartView.swift in Sources */, 8B89F523F97C8FEA7E1ABDDDC70DC93D /* RadarHighlighter.swift in Sources */, C07C7204DB8269F399C39E0247E7143C /* Range.swift in Sources */, 00A145728D4E065E52B4FA1E4ED43E12 /* Renderer.swift in Sources */, 75D6AAC3F5C75B4AD6B790BF8676C138 /* ScatterChartData.swift in Sources */, 1931C6D879091A0C2109B560F302E2C9 /* ScatterChartDataProvider.swift in Sources */, 003554553254D35F274111A634D0D2E1 /* ScatterChartDataSet.swift in Sources */, 2FB5A6CAC10BF11298827B4967922E16 /* ScatterChartRenderer.swift in Sources */, 4C8D108F68647323A8B42E3E55062050 /* ScatterChartView.swift in Sources */, 484F0E87FE13B0C3754B1622784DCBE3 /* SquareShapeRenderer.swift in Sources */, 9561A80B9A4A4780175A2D482F366D12 /* Transformer.swift in Sources */, 10ACA6864F5D3570A391788771649410 /* TransformerHorizontalBarChart.swift in Sources */, 360971EA6BC84CE3255F0B0E15FA41C3 /* TriangleShapeRenderer.swift in Sources */, 63D4F653030715FB89B82AE6B87861EA /* ViewPortHandler.swift in Sources */, 5248BCD7057BD67A7036E01B5B79AED6 /* ViewPortJob.swift in Sources */, 6A96E3270BCFF7B8583533A2CA6BA15A /* XAxis.swift in Sources */, CE3A62A1E007ECFDF50113D59DA80994 /* XAxisRenderer.swift in Sources */, 716C88D975F6E34C75F89BF4D0F2AEE4 /* XAxisRendererHorizontalBarChart.swift in Sources */, 5760D9C025FA510EABB7FC28FD9A8F25 /* XAxisRendererRadarChart.swift in Sources */, EA1A7688D149774765033557088632C3 /* XShapeRenderer.swift in Sources */, 15FBDF351099C60ED7ADF84655008D25 /* YAxis.swift in Sources */, BAB483EF709006D533591BD66064BB80 /* YAxisRenderer.swift in Sources */, BF02CF3DCB633921EB6F7D38321944C1 /* YAxisRendererHorizontalBarChart.swift in Sources */, BBAB4A8FC86B70A2A5D8D18B332426F7 /* YAxisRendererRadarChart.swift in Sources */, 54EFBF5D59D629C859C8737F55F0371D /* ZoomViewJob.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 6E8440064A8E450872C8E6B1824FDD84 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 3C023F772442591F73DEECDF1E0BCBB6 /* Pods-ChartsUnderstandAndUsage-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 29932759013A9009BE143BE54DB904B1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Charts; target = BF03B0B2B8B052092CB5F90CC5FB3757 /* Charts */; targetProxy = 53082718155BA30158B8DBD2D1315F84 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "POD_CONFIGURATION_DEBUG=1", "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; SYMROOT = "${SRCROOT}/../build"; }; name = Debug; }; 2DA47B55572DD819020BEEB3072D1F2A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 0394FC7CC3C3816823F09785CDB203C6 /* Charts.release.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREFIX_HEADER = "Target Support Files/Charts/Charts-prefix.pch"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MODULEMAP_FILE = "Target Support Files/Charts/Charts.modulemap"; PRODUCT_MODULE_NAME = Charts; PRODUCT_NAME = Charts; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 850A7429FD7701343A18A41F97E7FF1A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = E1B6524729FA9C069B58A8A2A01E166B /* Charts.debug.xcconfig */; buildSettings = { "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREFIX_HEADER = "Target Support Files/Charts/Charts-prefix.pch"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MODULEMAP_FILE = "Target Support Files/Charts/Charts.modulemap"; PRODUCT_MODULE_NAME = Charts; PRODUCT_NAME = Charts; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; C8642872F3C4F0EC44174AE764899B74 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 6F6C51CB6D62B862D7993889D948085C /* Pods-ChartsUnderstandAndUsage.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.modulemap"; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "POD_CONFIGURATION_RELEASE=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; SYMROOT = "${SRCROOT}/../build"; }; name = Release; }; F1643CC099A8E72145F989C7DFA39995 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = B2B47CCF8D645E2FBCF5AD3EF0709DDF /* Pods-ChartsUnderstandAndUsage.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.modulemap"; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( 25AD9454612BF454A1E3DC4CD4FA8C6D /* Debug */, CA547D2C7E9A8A153DC2B27FBE00B112 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D41945FCBF56EF7C8C86ADDA907EE076 /* Build configuration list for PBXNativeTarget "Charts" */ = { isa = XCConfigurationList; buildConfigurations = ( 850A7429FD7701343A18A41F97E7FF1A /* Debug */, 2DA47B55572DD819020BEEB3072D1F2A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; D6F23D399EE007FB2413D8F16EEF6AAC /* Build configuration list for PBXNativeTarget "Pods-ChartsUnderstandAndUsage" */ = { isa = XCConfigurationList; buildConfigurations = ( C8642872F3C4F0EC44174AE764899B74 /* Debug */, F1643CC099A8E72145F989C7DFA39995 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; } ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/Charts.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/Pods-ChartsUnderstandAndUsage.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/macbook.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState Charts.xcscheme isShown orderHint 0 Pods-ChartsUnderstandAndUsage.xcscheme isShown orderHint 1 SuppressBuildableAutocreation ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/zhanghaifeng.xcuserdatad/xcschemes/Charts.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/zhanghaifeng.xcuserdatad/xcschemes/Pods-ChartsUnderstandAndUsage.xcscheme ================================================ ================================================ FILE: Pods/Pods.xcodeproj/xcuserdata/zhanghaifeng.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState Charts.xcscheme isShown orderHint 0 Pods-ChartsUnderstandAndUsage.xcscheme isShown orderHint 1 SuppressBuildableAutocreation ================================================ FILE: Pods/Target Support Files/Charts/Charts-dummy.m ================================================ #import @interface PodsDummy_Charts : NSObject @end @implementation PodsDummy_Charts @end ================================================ FILE: Pods/Target Support Files/Charts/Charts-prefix.pch ================================================ #ifdef __OBJC__ #import #else #ifndef FOUNDATION_EXPORT #if defined(__cplusplus) #define FOUNDATION_EXPORT extern "C" #else #define FOUNDATION_EXPORT extern #endif #endif #endif ================================================ FILE: Pods/Target Support Files/Charts/Charts-umbrella.h ================================================ #ifdef __OBJC__ #import #else #ifndef FOUNDATION_EXPORT #if defined(__cplusplus) #define FOUNDATION_EXPORT extern "C" #else #define FOUNDATION_EXPORT extern #endif #endif #endif FOUNDATION_EXPORT double ChartsVersionNumber; FOUNDATION_EXPORT const unsigned char ChartsVersionString[]; ================================================ FILE: Pods/Target Support Files/Charts/Charts.debug.xcconfig ================================================ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Charts GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_ROOT = ${SRCROOT} PODS_TARGET_SRCROOT = ${PODS_ROOT}/Charts PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} SKIP_INSTALL = YES USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES ================================================ FILE: Pods/Target Support Files/Charts/Charts.modulemap ================================================ framework module Charts { umbrella header "Charts-umbrella.h" export * module * { export * } } ================================================ FILE: Pods/Target Support Files/Charts/Charts.release.xcconfig ================================================ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Charts GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_ROOT = ${SRCROOT} PODS_TARGET_SRCROOT = ${PODS_ROOT}/Charts PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} SKIP_INSTALL = YES USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES ================================================ FILE: Pods/Target Support Files/Charts/Charts.xcconfig ================================================ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Charts GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_ROOT = ${SRCROOT} PODS_TARGET_SRCROOT = ${PODS_ROOT}/Charts PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} SKIP_INSTALL = YES ================================================ FILE: Pods/Target Support Files/Charts/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType FMWK CFBundleShortVersionString 3.1.1 CFBundleSignature ???? CFBundleVersion ${CURRENT_PROJECT_VERSION} NSPrincipalClass ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType FMWK CFBundleShortVersionString 1.0.0 CFBundleSignature ???? CFBundleVersion ${CURRENT_PROJECT_VERSION} NSPrincipalClass ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType FMWK CFBundleShortVersionString 1.0.0 CFBundleSignature ???? CFBundleVersion ${CURRENT_PROJECT_VERSION} NSPrincipalClass ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-acknowledgements.markdown ================================================ # Acknowledgements This application makes use of the following third party libraries: ## Charts Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 Daniel Cohen Gindi & Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Generated by CocoaPods - https://cocoapods.org ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-acknowledgements.plist ================================================ PreferenceSpecifiers FooterText This application makes use of the following third party libraries: Title Acknowledgements Type PSGroupSpecifier FooterText Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016 Daniel Cohen Gindi & Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. License Apache License, Version 2.0 Title Charts Type PSGroupSpecifier FooterText Generated by CocoaPods - https://cocoapods.org Title Type PSGroupSpecifier StringsTable Acknowledgements Title Acknowledgements ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-dummy.m ================================================ #import @interface PodsDummy_Pods_ChartsUnderstandAndUsage : NSObject @end @implementation PodsDummy_Pods_ChartsUnderstandAndUsage @end ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks-Debug-input-files.xcfilelist ================================================ ${PODS_ROOT}/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks.sh ${BUILT_PRODUCTS_DIR}/Charts/Charts.framework ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks-Debug-output-files.xcfilelist ================================================ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Charts.framework ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks-Release-input-files.xcfilelist ================================================ ${PODS_ROOT}/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks.sh ${BUILT_PRODUCTS_DIR}/Charts/Charts.framework ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks-Release-output-files.xcfilelist ================================================ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Charts.framework ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-frameworks.sh ================================================ #!/bin/sh set -e set -u set -o pipefail function on_error { echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" } trap 'on_error $LINENO' ERR if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy # frameworks to, so exit 0 (signalling the script phase was successful). exit 0 fi echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" BCSYMBOLMAP_DIR="BCSymbolMaps" # This protects against multiple targets copying the same framework dependency at the same time. The solution # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") # Copies and strips a vendored framework install_framework() { if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then local source="${BUILT_PRODUCTS_DIR}/$1" elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" elif [ -r "$1" ]; then local source="$1" fi local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" if [ -L "${source}" ]; then echo "Symlinked..." source="$(readlink "${source}")" fi if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do echo "Installing $f" install_bcsymbolmap "$f" "$destination" rm "$f" done rmdir "${source}/${BCSYMBOLMAP_DIR}" fi # Use filter instead of exclude so missing patterns don't throw errors. echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" local basename basename="$(basename -s .framework "$1")" binary="${destination}/${basename}.framework/${basename}" if ! [ -r "$binary" ]; then binary="${destination}/${basename}" elif [ -L "${binary}" ]; then echo "Destination binary is symlinked..." dirname="$(dirname "${binary}")" binary="${dirname}/$(readlink "${binary}")" fi # Strip invalid architectures so "fat" simulator / device frameworks work on device if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then strip_invalid_archs "$binary" fi # Resign the code if required by the build settings to avoid unstable apps code_sign_if_enabled "${destination}/$(basename "$1")" # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then local swift_runtime_libs swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) for lib in $swift_runtime_libs; do echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" code_sign_if_enabled "${destination}/${lib}" done fi } # Copies and strips a vendored dSYM install_dsym() { local source="$1" warn_missing_arch=${2:-true} if [ -r "$source" ]; then # Copy the dSYM into the targets temp dir. echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" local basename basename="$(basename -s .dSYM "$source")" binary_name="$(ls "$source/Contents/Resources/DWARF")" binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" # Strip invalid architectures from the dSYM. if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then strip_invalid_archs "$binary" "$warn_missing_arch" fi if [[ $STRIP_BINARY_RETVAL == 0 ]]; then # Move the stripped file into its final destination. echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" else # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" fi fi } # Used as a return value for each invocation of `strip_invalid_archs` function. STRIP_BINARY_RETVAL=0 # Strip invalid architectures strip_invalid_archs() { binary="$1" warn_missing_arch=${2:-true} # Get architectures for current target binary binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" # Intersect them with the architectures we are building for intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" # If there are no archs supported by this binary then warn the user if [[ -z "$intersected_archs" ]]; then if [[ "$warn_missing_arch" == "true" ]]; then echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." fi STRIP_BINARY_RETVAL=1 return fi stripped="" for arch in $binary_archs; do if ! [[ "${ARCHS}" == *"$arch"* ]]; then # Strip non-valid architectures in-place lipo -remove "$arch" -output "$binary" "$binary" stripped="$stripped $arch" fi done if [[ "$stripped" ]]; then echo "Stripped $binary of architectures:$stripped" fi STRIP_BINARY_RETVAL=0 } # Copies the bcsymbolmap files of a vendored framework install_bcsymbolmap() { local bcsymbolmap_path="$1" local destination="${BUILT_PRODUCTS_DIR}" echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" } # Signs a framework with the provided identity code_sign_if_enabled() { if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then # Use the current code_sign_identity echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then code_sign_cmd="$code_sign_cmd &" fi echo "$code_sign_cmd" eval "$code_sign_cmd" fi } if [[ "$CONFIGURATION" == "Debug" ]]; then install_framework "${BUILT_PRODUCTS_DIR}/Charts/Charts.framework" fi if [[ "$CONFIGURATION" == "Release" ]]; then install_framework "${BUILT_PRODUCTS_DIR}/Charts/Charts.framework" fi if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then wait fi ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-resources.sh ================================================ #!/bin/sh set -e set -u set -o pipefail if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy # resources to, so exit 0 (signalling the script phase was successful). exit 0 fi mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt > "$RESOURCES_TO_COPY" XCASSET_FILES=() # This protects against multiple targets copying the same framework dependency at the same time. The solution # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") case "${TARGETED_DEVICE_FAMILY:-}" in 1,2) TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" ;; 1) TARGET_DEVICE_ARGS="--target-device iphone" ;; 2) TARGET_DEVICE_ARGS="--target-device ipad" ;; 3) TARGET_DEVICE_ARGS="--target-device tv" ;; 4) TARGET_DEVICE_ARGS="--target-device watch" ;; *) TARGET_DEVICE_ARGS="--target-device mac" ;; esac install_resource() { if [[ "$1" = /* ]] ; then RESOURCE_PATH="$1" else RESOURCE_PATH="${PODS_ROOT}/$1" fi if [[ ! -e "$RESOURCE_PATH" ]] ; then cat << EOM error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. EOM exit 1 fi case $RESOURCE_PATH in *.storyboard) echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} ;; *.xib) echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} ;; *.framework) echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" ;; *.xcdatamodel) echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" ;; *.xcdatamodeld) echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" ;; *.xcmappingmodel) echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" ;; *.xcassets) ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") ;; *) echo "$RESOURCE_PATH" || true echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" ;; esac } mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" fi rm -f "$RESOURCES_TO_COPY" if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] then # Find all other xcassets (this unfortunately includes those of path pods and other targets). OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) while read line; do if [[ $line != "${PODS_ROOT}*" ]]; then XCASSET_FILES+=("$line") fi done <<<"$OTHER_XCASSETS" if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" else printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" fi fi ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage-umbrella.h ================================================ #ifdef __OBJC__ #import #else #ifndef FOUNDATION_EXPORT #if defined(__cplusplus) #define FOUNDATION_EXPORT extern "C" #else #define FOUNDATION_EXPORT extern #endif #endif #endif FOUNDATION_EXPORT double Pods_ChartsUnderstandAndUsageVersionNumber; FOUNDATION_EXPORT const unsigned char Pods_ChartsUnderstandAndUsageVersionString[]; ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.debug.xcconfig ================================================ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Charts" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Charts/Charts.framework/Headers" LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -framework "Charts" OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.modulemap ================================================ framework module Pods_ChartsUnderstandAndUsage { umbrella header "Pods-ChartsUnderstandAndUsage-umbrella.h" export * module * { export * } } ================================================ FILE: Pods/Target Support Files/Pods-ChartsUnderstandAndUsage/Pods-ChartsUnderstandAndUsage.release.xcconfig ================================================ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Charts" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Charts/Charts.framework/Headers" LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -framework "Charts" OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES ================================================ FILE: README.md ================================================ # ChartsUnderstandAndUsage ## 这是一个对图形框架Charts的理解使用demo(后续会根据自己理解,持续更新) 效果展示 ![](./ChartsUnderstandAndUsage/效果图.gif) ## Demo的使用: ### 该demo包含了Charts框架的 每种图形使用都有详细的注释。(方便理解、调整成自己需要的图形展示) (柱状图、饼状图、饼状图(半圆形)、雷达图、折线图、折线填充图、散点图、K 线图(烛形图)、气泡图、组合图(混合图)的基本使用方法 #### 1.柱状图 BarChartVC.swift #### 2.柱状图(波浪图)BarChartWaveVC.swift //添加柱状图 addBarChartView() //设置基本样式 setBarChartViewBaseStyle() //设置X轴,Y轴样式 setBarChartViewXY() //添加(刷新数据)updataData() #### 3.饼状图 PieChartVC.swift #### 4.饼状图(半圆形)PieChartHalfVC.swift #### 5.饼状图(折线注释)PieChartPolylineVC.swift //添加饼状图 addPieChart() //设置基本样式 setPieChartViewBaseStyle() //添加(刷新数据) updataData() #### 6.雷达图 RadarChartVC.swift (该图在刷新数据时要重新设置基本样式,否则会越来越小) //添加雷达图 addRadarChart() //设置基本样式 setRadarChartViewBaseStyle() #### 7.折线图.swift //添加折线 addLineChart() //折线图描述文字和样式 chartDescription() //设置交互样式 interactionStyle() //修改背景色和边框样式 setBackgroundBorder() //设置x轴的样式属性 setXAxisStyle() //设置y轴的样式属性 setYAxisStyle() //设置限制线(可设置多根)setlimitLine() //添加(刷新数据) updataData() #### 8.折线填充图 LineFilledChartVC.swift //添加折线 addLineChart() //设置基本样式 setLineChartViewBaseStyle() //添加(刷新数据) updataData() #### 9.散点图 ScatterChartVC.swift //添加散点图 addScatterChart() //基本样式 setScatterChartViewBaseStyle() //添加(刷新数据)updataData() #### 10.K 线图(烛形图)CandleStickChartVC.swift //添加K 线图(烛形图) addCandleStickChart() //基本样式 setCandleStickChartViewBaseStyle() //添加(刷新数据)updataData() #### 11.气泡图 BubbleChartVC.swift //添加气泡图 addBubbleChart() //基本样式 setBubbleChartViewBaseStyle() //添加(刷新数据)updataData() #### 12.组合图(混合图)的基本使用方法 CombinedChartVC.swift //添加混合图 addCombinedChart() //基本样式 setCombinedChartViewBaseStyle() //添加(刷新数据)updataData() #### 13.波浪图 WaveformChartVC.swift (该波形图主要是针对给一组数据,根据制高点和波形宽来画一连串波形) //添加折线 addLineChart() //设置基本样式 setLineChartViewBaseStyle() setXAxisStyle() setYAxisStyle() //添加(刷新数据)updataData() # PS 欢迎fork,star. 该demo地址: ` https://github.com/FighterLightning/ChartsUnderstandAndUsage.git