Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // ChartViewController.swift
- // Emiltonia
- //Age P3 P15 P50 P85 P97
- import UIKit
- import Charts
- fileprivate let yLabelRectInset = CGFloat(4)
- fileprivate let yLabelForegroundColor = UIColor.white
- fileprivate let yLabelBackgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
- fileprivate let labelFont = Theme.Fonts.Controls.compact
- fileprivate let labelColor = Theme.Colors.Text.inputTextGray
- fileprivate let lineColorRed = Theme.Colors.Stroke.emiltoniaRed
- fileprivate let lineColorGreen = Theme.Colors.Stroke.emiltoniaGreen
- fileprivate let chartBackgroundColor = UIColor(red: 0.97, green: 0.97, blue: 0.97, alpha: 1)
- fileprivate let chartGridColor = UIColor(red: 0.80, green: 0.80, blue: 0.80, alpha: 1)
- fileprivate let chartBorderColor = UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1)
- fileprivate let statisticColor = UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1)
- fileprivate let dashes = [
- [CGFloat(4), CGFloat(8)],
- [CGFloat(6), CGFloat(6)],
- [CGFloat(8), CGFloat(4)],
- [CGFloat(6), CGFloat(6)],
- [CGFloat(4), CGFloat(8)]
- ]
- fileprivate let dashWidth = CGFloat(1.5)
- fileprivate let babyLineWidth = CGFloat(2)
- class ChartViewController: UIViewController, ChartViewDelegate {
- private(set) var dataProvider:ChartDataProvider!
- var chartView:ChartView {
- return self.view as! ChartView
- }
- override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
- return .portrait
- }
- override func viewDidLoad() {
- super.viewDidLoad()
- setupChart()
- setupBabyLine()
- self.chartView.shareScreenButton.addTarget(self, action: #selector(didTapShare(_:)), for: .touchUpInside)
- }
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- setupLegend()
- }
- fileprivate func setupLegend() {
- let legend = self.chartView.lineChartView.legend
- legend.enabled = false
- let chv = self.chartView
- let baby = self.dataProvider.baby
- chv.babyLegend.label.text = baby.name
- chv.layoutIfNeeded()
- let wConst = chv.babyLegend.bounds.size.width * 0.8
- if chv.babyLegend.label.bounds.size.width >= wConst {
- chv.babyLegend.label.widthAnchor.constraint(equalToConstant: wConst).isActive = true
- chv.layoutIfNeeded()
- }
- chv.babyLegend.addDashedLine(width: babyLineWidth, pattern: [CGFloat](), color: self.babyChartColor(baby).first!)
- chv.p3Legend.addDashedLine(width: dashWidth, pattern: dashes[0], color: statisticColor)
- chv.p15Legend.addDashedLine(width: dashWidth, pattern: dashes[1], color: statisticColor)
- chv.p50Legend.addDashedLine(width: dashWidth, pattern: dashes[2], color: statisticColor)
- chv.p85Legend.addDashedLine(width: dashWidth, pattern: dashes[3], color: statisticColor)
- chv.p97Legend.addDashedLine(width: dashWidth, pattern: dashes[4], color: statisticColor)
- }
- fileprivate func setupChart() {
- self.chartView.backgroundColor = chartBackgroundColor
- let lineChartView = self.chartView.lineChartView!
- lineChartView.backgroundColor = chartBackgroundColor
- lineChartView.borderColor = chartBorderColor
- //lineChartView.borderLineWidth = 1
- lineChartView.drawBordersEnabled = true
- lineChartView.dragXEnabled = true
- lineChartView.dragYEnabled = true//false
- lineChartView.scaleXEnabled = true
- lineChartView.scaleYEnabled = true//false
- lineChartView.viewPortHandler.setMaximumScaleX(4)
- lineChartView.viewPortHandler.setMaximumScaleY(2)
- lineChartView.delegate = self
- let xValueRenderer = XValueRenderer(
- viewPortHandler: lineChartView.viewPortHandler,
- xAxis: lineChartView.xAxis,
- transformer: lineChartView.xAxisRenderer.transformer
- )
- let baby = self.dataProvider.baby
- xValueRenderer.birthColor = self.babyChartColor(baby).first!
- lineChartView.xAxisRenderer = xValueRenderer
- let xAxis = lineChartView.xAxis
- xAxis.labelPosition = .bottom
- xAxis.gridColor = chartGridColor
- xAxis.labelFont = labelFont
- xAxis.labelTextColor = labelColor
- xAxis.valueFormatter = XValueFormatter()
- xAxis.labelCount = UIScreen.main.bounds.size.width > 375 ? 5 : 4
- xAxis.axisMinimum = self.dataProvider.leftXLimit
- xAxis.axisMaximum = self.dataProvider.rightXLimit
- lineChartView.getAxis(.right).enabled = false
- let yValueRenderer = YValueRenderer(
- viewPortHandler: lineChartView.viewPortHandler,
- yAxis: lineChartView.getAxis(.left),
- transformer: lineChartView.xAxisRenderer.transformer
- )
- lineChartView.leftYAxisRenderer = yValueRenderer
- let yAxis = lineChartView.getAxis(.left)
- yAxis.labelPosition = .insideChart
- yAxis.drawTopYLabelEntryEnabled = false
- yAxis.drawBottomYLabelEntryEnabled = false
- yAxis.axisMinimum = self.dataProvider.lowerYLimit
- yAxis.axisMaximum = self.dataProvider.upperYLimit
- yAxis.labelCount = Int((yAxis.axisMaximum - yAxis.axisMinimum) / self.dataProvider.yStep)
- yAxis.gridColor = chartGridColor
- yAxis.labelFont = labelFont
- yAxis.labelTextColor = yLabelForegroundColor
- yAxis.valueFormatter = YValueFormatter()
- yAxis.xOffset = yAxis.xOffset + yLabelRectInset
- }
- fileprivate func setupBabyLine() {
- let baby = self.dataProvider.baby
- let line = LineChartDataSet(values: self.dataProvider.babyDataEntry, label: baby.name)
- line.colors = babyChartColor(baby).map{ NSUIColor(cgColor: $0.cgColor) }
- line.circleColors = [NSUIColor(cgColor: UIColor.white.cgColor)]
- line.circleHoleColor = line.colors[0]
- line.lineWidth = babyLineWidth
- line.circleRadius = line.lineWidth * 3
- line.circleHoleRadius = line.circleRadius * 0.8
- line.axisDependency = .left
- line.drawValuesEnabled = false
- line.drawVerticalHighlightIndicatorEnabled = false
- line.drawHorizontalHighlightIndicatorEnabled = false
- let data = LineChartData()
- statisticLines().forEach {
- data.addDataSet($0)
- }
- data.addDataSet(line)
- self.chartView.lineChartView.data = data
- self.chartView.lineChartView.chartDescription = nil
- }
- fileprivate func statisticLines() -> [LineChartDataSet] {
- let data = self.dataProvider!
- let entries = [
- data.statisticP3Entry,
- data.statisticP15Entry,
- data.statisticP50Entry,
- data.statisticP85Entry,
- data.statisticP97Entry
- ]
- var chartDataSets = [LineChartDataSet]()
- for i in 0..<entries.count {
- let line = LineChartDataSet(values: entries[i], label: "")
- line.colors = [NSUIColor(cgColor: statisticColor.cgColor)]
- line.drawCircleHoleEnabled = false
- line.drawCirclesEnabled = false
- line.axisDependency = .left
- line.drawValuesEnabled = false
- line.drawVerticalHighlightIndicatorEnabled = false
- line.drawHorizontalHighlightIndicatorEnabled = false
- line.lineDashLengths = dashes[i]
- line.lineWidth = dashWidth
- chartDataSets.append(line)
- }
- return chartDataSets
- }
- func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
- recalculateXLimits()
- }
- func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) {
- recalculateXLimits()
- }
- func recalculateXLimits() {
- let lineChartView = self.chartView.lineChartView!
- let minX = -Double(4.0 / lineChartView.scaleX)
- if lineChartView.lowestVisibleX < minX {
- lineChartView.moveViewToX(minX)
- lineChartView.setNeedsDisplay()
- }
- }
- fileprivate func babyChartColor(_ baby:Baby) -> [UIColor] {
- var colors = [UIColor]()
- if baby.gender == .male {
- colors.append(lineColorGreen)
- } else {
- colors.append(lineColorRed)
- }
- return colors
- }
- class YValueFormatter: IndexAxisValueFormatter {
- override func stringForValue(_ value: Double, axis: AxisBase?) -> String {
- var s = "\(Int(value.rounded()))"
- if s.count > 3 {
- s.insert(".", at: String.Index(encodedOffset: s.count - 3))
- }
- return s
- }
- }
- class XValueFormatter: IndexAxisValueFormatter {
- let weeksStr = CommonStrings.Units.weeks.localized.lowercased()
- override func stringForValue(_ value: Double, axis: AxisBase?) -> String {
- if value == 0 {
- return "kBirthday"
- }
- if value < 1 {
- return ""
- }
- let i = Int(value.rounded())
- let s = "\(i) \(weeksStr)"
- return s
- }
- }
- class XValueRenderer: XAxisRenderer {
- let birthStr = CommonStrings.Units.birthday.localized
- let birthImgSize = CGFloat(15)
- private(set) var birthImg:UIImage? = nil
- var birthColor: UIColor? = nil {
- didSet {
- if let col = birthColor, let img = UIImage(named: "birth_image") {
- birthImg = img.imageWithColor(color: col)
- } else {
- birthImg = nil
- }
- }
- }
- override func drawLabel(context: CGContext, formattedLabel: String, x: CGFloat, y: CGFloat, attributes: [NSAttributedStringKey : Any], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) {
- if formattedLabel == "kBirthday" {
- var mAttributes = attributes
- if let col = birthColor {
- mAttributes[NSAttributedStringKey.foregroundColor] = col
- }
- if let font = mAttributes[NSAttributedStringKey.font] as? UIFont {
- let name = font.fontName.replacingOccurrences(of: "Regular", with: "SemiBold")
- mAttributes[NSAttributedStringKey.font] = UIFont(name: name, size: font.pointSize) ?? font
- }
- if let img = birthImg {
- UIGraphicsPushContext(context)
- img.draw(in: CGRect(x: x - birthImgSize/2, y: viewPortHandler.contentBottom - birthImgSize/2, width: birthImgSize, height: birthImgSize))
- UIGraphicsPopContext()
- }
- super.drawLabel(context: context, formattedLabel: birthStr, x: x, y: y, attributes: mAttributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians)
- return
- }
- super.drawLabel(context: context, formattedLabel: formattedLabel, x: x, y: y, attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians)
- }
- }
- class YValueRenderer: YAxisRenderer {
- 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
- }
- }
- let fixedPosition = xPos
- let positions = transformedPositions()
- let offset = yoffset - yAxis.labelFont.lineHeight
- let from = yAxis.isDrawBottomYLabelEntryEnabled ? 0 : 1
- let to = yAxis.isDrawTopYLabelEntryEnabled ? yAxis.entryCount : (yAxis.entryCount - 1)
- let attributes = [NSAttributedStringKey.font: labelFont]
- let col = yLabelBackgroundColor
- for i in stride(from: from, to: to, by: 1) {
- let text = yAxis.getFormattedLabel(i)
- var point = CGPoint(x: fixedPosition, y: positions[i].y + offset)
- let textSize = text.size(withAttributes: attributes)
- if textAlign == .center {
- point.x -= textSize.width / 2.0
- } else if textAlign == .right {
- point.x -= textSize.width
- }
- let rect = CGRect(origin: point, size: textSize).insetBy(dx: -yLabelRectInset, dy: 0)
- let path = UIBezierPath.init(roundedRect: rect, cornerRadius: yLabelRectInset)
- UIGraphicsPushContext(context)
- col.set()
- path.fill()
- UIGraphicsPopContext()
- }
- super.renderAxisLabels(context: context)
- }
- }
- }
- extension ChartViewController: ExtendedViewController {
- static func viewController(with dataProvider: Any?) throws -> UIViewController {
- guard let dataProvider = dataProvider as? ChartDataProvider else {
- throw ExtendedViewControllerError.unknownDataProvider
- }
- let storyboard = UIStoryboard(name: "Chart", bundle: nil)
- let vc = storyboard.instantiateInitialViewController() as! ChartViewController
- vc.dataProvider = dataProvider
- return vc
- }
- func present(at primaryViewController: UIViewController) throws {
- guard let navigationController = primaryViewController.navigationController else {
- throw ExtendedViewControllerError.presentationFailed
- }
- DispatchQueue.main.async {
- navigationController.pushViewController(self, animated: primaryViewController.isActiveAndVisible)
- }
- }
- func dismiss(onClose:((_ dataProvider:Any?)->())?) throws {
- throw ExtendedViewControllerError.emptyMethod
- }
- func navigationTitleText() -> String {
- return "ChartViewController".localized(tableName: "NavigationTitle")
- }
- @objc func didTapShare(_ sender:Any?) {
- let v = self.chartView
- let renderer = UIGraphicsImageRenderer(size: v.bounds.size)
- let image = renderer.image { ctx in
- v.drawHierarchy(in: v.bounds, afterScreenUpdates: true)
- }
- let vc = UIActivityViewController(
- activityItems: [image.crop(rect: self.chartView.captureRect)],
- applicationActivities: []
- )
- present(vc, animated: true)
- }
- }
- extension UIImage {
- func crop( rect: CGRect) -> UIImage {
- var rect = rect
- rect.origin.x*=self.scale
- rect.origin.y*=self.scale
- rect.size.width*=self.scale
- rect.size.height*=self.scale
- let imageRef = self.cgImage!.cropping(to: rect)
- let image = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
- return image
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement