Skip to content

Instantly share code, notes, and snippets.

@ivnsch
Last active August 20, 2018 00:09
Show Gist options
  • Save ivnsch/f6e285eb0c86bb831510 to your computer and use it in GitHub Desktop.
Save ivnsch/f6e285eb0c86bb831510 to your computer and use it in GitHub Desktop.
Chart with autolayout
// NOTE: you may have to set the module in the storyboard to "SwiftCharts", otherwise the view may not be recognized correctly, which leads to axis, labels and guidelines not showing
class HelloWorld: UIViewController {
private var chart: Chart? // arc
@IBOutlet weak var chartView: ChartBaseView!
private var didLayout: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !self.didLayout {
self.didLayout = true
self.initChart()
}
}
private func initChart() {
// map model data to chart points
let chartPoints: [ChartPoint] = [(2, 2), (4, 4), (6, 6), (8, 10), (12, 14)].map{ChartPoint(x: ChartAxisValueInt($0.0), y: ChartAxisValueInt($0.1))}
let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont)
// define x and y axis values (quick-demo way, see other examples for generation based on chartpoints)
let xValues = stride(from: 0, through: 16, by: 2).map {ChartAxisValueInt($0, labelSettings: labelSettings)}
let yValues = stride(from: 0, through: 16, by: 2).map {ChartAxisValueInt($0, labelSettings: labelSettings)}
// create axis models with axis values and axis title
let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings))
let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical()))
let chartFrame = self.chartView.frame
// generate axes layers and calculate chart inner frame, based on the axis models
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: ExamplesDefaults.chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel)
let (xAxis, yAxis, innerFrame) = (coordsSpace.xAxis, coordsSpace.yAxis, coordsSpace.chartInnerFrame)
// create layer with guidelines
let guidelinesLayerSettings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth)
let guidelinesLayer = ChartGuideLinesDottedLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, settings: guidelinesLayerSettings)
// view generator - this is a function that creates a view for each chartpoint
let viewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in
let viewSize: CGFloat = Env.iPad ? 30 : 20
let center = chartPointModel.screenLoc
let label = UILabel(frame: CGRect(x: center.x - viewSize / 2, y: center.y - viewSize / 2, width: viewSize, height: viewSize))
label.backgroundColor = UIColor.green
label.textAlignment = NSTextAlignment.center
label.text = "\(chartPointModel.chartPoint.y.description)"
label.font = ExamplesDefaults.labelFont
return label
}
// create layer that uses viewGenerator to display chartpoints
let chartPointsLayer = ChartPointsViewsLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, chartPoints: chartPoints, viewGenerator: viewGenerator)
// create chart instance with frame and layers
let chart = Chart(
view: self.chartView!,
layers: [
coordsSpace.xAxis,
coordsSpace.yAxis,
guidelinesLayer,
chartPointsLayer
]
)
self.chart = chart
}
func rotated() {
for view in self.chartView.subviews {
view.removeFromSuperview()
}
self.initChart()
}
}
@sniis84
Copy link

sniis84 commented Aug 13, 2015

lines 26 and 27 should include into an Array() definition

@sniis84
Copy link

sniis84 commented Aug 13, 2015

ios simulator screen shot 13 ago 2015 14 50 44

The image is completely stretched! The proportions are not kept!

@ivnsch
Copy link
Author

ivnsch commented Aug 14, 2015

I'm using the swift2.0 branch (Swift 2.0 doesn't require Array() anymore).

About the stretched image, this happens on rotation change, because the code didn't handle it. Added the handling, should be fixed now.

@sniis84
Copy link

sniis84 commented Aug 15, 2015

Genius! That's amazing. Now rotation works perfectly 👍
I'm sorry, I don't like using betas so I didn't know about swift2.0 and the change in Array(stride:) definition.
How can I quote your library in my App?

@reivax
Copy link

reivax commented Jan 3, 2016

Hey there - I'm using this sample code in my app and if I call initChart after the view is rendered (after getting some data from a server) only the curve layer is displayed (the axis are not) - any idea of what I'm doing wrong?

here is how you can repro the issue using latest sample code:

  • add a UIView to the iPhone storyboard in DetailView Controller/View
    -> call view chartView and make it an outlet to the Detail class
  • update (aka hack) DetailViewController.swift as follow (mainly using the sample code from above)
//
//  DetailViewController.swift
//  SwiftCharts
//
//  Created by ischuetz on 20/04/15.
//  Copyright (c) 2015 ivanschuetz. All rights reserved.
//

import UIKit
import SwiftCharts

class DetailViewController: UIViewController, UISplitViewControllerDelegate {

    @IBOutlet weak var detailDescriptionLabel: UILabel!
    @IBOutlet weak var chartView: ChartBaseView!

//    lazy var chartFrame: CGRect! = {
//        CGRectMake(0, 80, self.view.frame.size.width, self.view.frame.size.height - 80)
//    }()

    var detailItem: Example? {
        didSet {
            self.configureView()
            //initChart()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        var timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "initChart", userInfo: nil, repeats: false)
    }

    func initChart() {
        // map model data to chart points
        let chartPoints: [ChartPoint] = [(2, 2), (4, 4), (6, 6), (8, 10), (12, 14)].map{ChartPoint(x: ChartAxisValueInt($0.0), y: ChartAxisValueInt($0.1))}

        let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont)

        // define x and y axis values (quick-demo way, see other examples for generation based on chartpoints)
        let xValues = 0.stride( through: 16, by: 2).map {ChartAxisValueInt($0, labelSettings: labelSettings)}
        let yValues = 0.stride( through: 16, by: 2).map {ChartAxisValueInt($0, labelSettings: labelSettings)}

        // create axis models with axis values and axis title
        let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings))
        let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical()))

        let chartFrame = self.chartView.frame

        // generate axes layers and calculate chart inner frame, based on the axis models
        let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: ExamplesDefaults.chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel)
        let (xAxis, yAxis, innerFrame) = (coordsSpace.xAxis, coordsSpace.yAxis, coordsSpace.chartInnerFrame)

        // create layer with guidelines
        let guidelinesLayerSettings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.blackColor(), linesWidth: ExamplesDefaults.guidelinesWidth)
        let guidelinesLayer = ChartGuideLinesDottedLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, settings: guidelinesLayerSettings)

        // view generator - this is a function that creates a view for each chartpoint
        let viewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in
            let viewSize: CGFloat = Env.iPad ? 30 : 20
            let center = chartPointModel.screenLoc
            let label = UILabel(frame: CGRectMake(center.x - viewSize / 2, center.y - viewSize / 2, viewSize, viewSize))
            label.backgroundColor = UIColor.greenColor()
            label.textAlignment = NSTextAlignment.Center
            label.text = "\(chartPointModel.chartPoint.y)"
            label.font = ExamplesDefaults.labelFont
            return label
        }

        // create layer that uses viewGenerator to display chartpoints
        let chartPointsLayer = ChartPointsViewsLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, chartPoints: chartPoints, viewGenerator: viewGenerator)

        // create chart instance with frame and layers
        let chart = Chart(
            view: self.chartView!,
            layers: [
                coordsSpace.xAxis,
                coordsSpace.yAxis,
                guidelinesLayer,
                chartPointsLayer
            ]
        )

        self.chartView.setNeedsDisplay()
        //self.chart = chart
    }

    var currentExampleController: UIViewController?

    func configureView() {

        if let example: Example = self.detailItem  {
            switch example {
            case .HelloWorld:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(HelloWorld())
            case .Bars:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(BarsExample())
            case .StackedBars:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(StackedBarsExample())
            case .BarsPlusMinus:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(BarsPlusMinusWithGradientExample())
            case .GroupedBars:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(GroupedBarsExample())
            case .BarsStackedGrouped:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(GroupedAndStackedBarsExample())
            case .Scatter:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(ScatterExample())
            case .Notifications:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(NotificationsExample())
            case .Target:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(TargetExample())
            case .Areas:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(AreasExample())
            case .Bubble:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(BubbleExample())
            case .Combination:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(BarsPlusMinusAndLinesExample())
            case .Scroll:
                self.setSplitSwipeEnabled(false)
                self.automaticallyAdjustsScrollViewInsets = false
                self.showExampleController(ScrollExample())
            case .Coords:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(CoordsExample())
            case .Tracker:
                self.setSplitSwipeEnabled(false)
                self.showExampleController(TrackerExample())
            case .EqualSpacing:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(EqualSpacingExample())
            case .CustomUnits:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(CustomUnitsExample())
            case .Multival:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(MultipleLabelsExample())
            case .MultiAxis:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(MultipleAxesExample())
            case .MultiAxisInteractive:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(MultipleAxesInteractiveExample())
            case .CandleStick:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(CandleStickExample())
            case .Cubiclines:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(CubicLinesExample())
            case .NotNumeric:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(NotNumericExample())
            case .CandleStickInteractive:
                self.setSplitSwipeEnabled(false)
                self.showExampleController(CandleStickInteractiveExample())
            case .Trendline:
                self.setSplitSwipeEnabled(true)
                self.showExampleController(TrendlineExample())
            }
        }
    }

    private func showExampleController(controller: UIViewController) {
        if let currentExampleController = self.currentExampleController {
            currentExampleController.removeFromParentViewController()
            currentExampleController.view.removeFromSuperview()
        }
        self.addChildViewController(controller)
        //self.view.addSubview(controller.view)
        self.currentExampleController = controller
    }

    private func setSplitSwipeEnabled(enabled: Bool) {
        if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
            let splitViewController = UIApplication.sharedApplication().delegate?.window!!.rootViewController as! UISplitViewController
            splitViewController.presentsWithGesture = enabled
        }
    }
}

results: click on any example, details view is shown - results: after 2 sec the graph is shown but the axis layers are not shown... self.chartView.setNeedsDisplay seems to have no effect - Any idea of what I'm missing? Thanks!

@fczelkamaya
Copy link

Hi! How can I add this: "@IBOutlet weak var chartView: ChartBaseView!" ? I not see ChartBaseView when i drag UIView to the class and I can't convert my UIView to ChartBaseView. Can somebody please help me? Thanks!

@fczelkamaya
Copy link

Hi, I tried to rewrite UIView to ChartBaseView to IBOutlet and I have the same problem as reivax (axis are missing). Did you resolve this? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment