Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import UIKit
import SwiftCharts
// 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 AnalysisController: BaseViewController {
let monthData: [Int: [String: Float]] = [0: ["Jan": 20],
1: ["Feb": 4],
2: ["Mar": 20],
3: ["Apr": 83],
4: ["May": 750],
5: ["Jun": 400],
6: ["Jul": 300],
7: ["Aug": 49],
8: ["Sep": 400],
9: ["Oct": 49],
10: ["Nov": 39],
11: ["Dec": 10]
]
private var chart: Chart?
private var didLayout: Bool = false
let backgroundView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.masksToBounds = false
view.layer.shadowOpacity = 0.23
view.layer.shadowColor = UIColor(hex: "B1B2B3").cgColor
view.layer.shadowRadius = 4 // blur
view.layer.shadowOffset = CGSize(width: 0, height: 2) // Spread
return view
}()
let monthlyTotalView: MonthlySpendingView = {
let view = MonthlySpendingView()
return view
}()
fileprivate var chartView: ChartBaseView = {
let view = ChartBaseView()
view.backgroundColor = .clear
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
//let currencyTextField = TSCurrencyTextField()
var counter = 0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if counter < 2 {
self.initChart()
self.setupMonthlySpendingView()
chart?.zoom(scaleX: 2.4, scaleY: 4.8, anchorX: 0, anchorY: 1)
counter += 1
}
}
private func getMonthData() -> [MonthData] {
var items = [MonthData]()
for (index, data) in monthData {
for (monthName, expenses) in data {
let monthData = MonthData(index: index, monthName: monthName, expenses: expenses)
items.append(monthData)
}
}
return items.sorted(by: {$0.index < $1.index})
}
private func initChart() {
self.setupChartPosition()
// map model data to chart points
let xLabelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont, fontColor: ExamplesDefaults.labelTextColor, rotation: 0, rotationKeep: .top, shiftXOnRotation: false, textAlignment: .default)
let yLabelSettings = ChartLabelSettings(font: ExamplesDefaults.semiBoldFont, fontColor: ExamplesDefaults.labelTextColor, rotation: 0, rotationKeep: .center, shiftXOnRotation: false, textAlignment: .default)
let chartPoints: [ChartPoint] = getMonthData().enumerated().map {index, item in
let x = ChartAxisValueString(item.monthName, order: index, labelSettings: xLabelSettings)
let y = ChartAxisValueString("", order: Int(item.expenses), labelSettings: yLabelSettings)
return ChartPoint(x: x, y: y)
}
let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont, fontColor: ExamplesDefaults.labelTextColor, rotation: 0, rotationKeep: .center, shiftXOnRotation: false, textAlignment: .default)
// define x and y axis values (quick-demo way, see other examples for generation based on chartpoints)
let xValues = getMonthData().enumerated().map {index, item -> ChartAxisValueString in
return ChartAxisValueString(item.monthName, order: index, labelSettings: labelSettings)
}
let yValues = stride(from: 0, through: 10000, by: 200).map { (integer) -> ChartAxisValueString in
return ChartAxisValueString("$\(integer)", order: integer, labelSettings: labelSettings)
}
// create axis
//with axis values and axis title
let yModel = ChartAxisModel(axisValues: yValues, lineColor: .clear, axisTitleLabel: ChartAxisLabel(text: "", settings: labelSettings.defaultVertical()))
let xModel = ChartAxisModel(axisValues: xValues, lineColor: .clear, axisTitleLabel: ChartAxisLabel(text: "", settings: labelSettings.defaultVertical()), labelsConflictSolver: nil, leadingPadding: .fixed(13), trailingPadding: .fixed(13), labelSpaceReservationMode: AxisLabelsSpaceReservationMode.current, clipContents: false)
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.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)
// create layer with guidelines
let settings = ChartGuideLinesLayerSettings(linesColor: ExamplesDefaults.guidelinesColor, linesWidth: ExamplesDefaults.guidelinesWidth)
let guidelinesLayer = ChartGuideLinesLayer(xAxisLayer: xAxis, yAxisLayer: yAxis, axis: .y, settings: settings)
// view generator - this is a function that creates a view for each chartpoint
let barViewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in
let bottomLeft = layer.modelLocToScreenLoc(x: 0, y: 0)
let barWidth: CGFloat = Env.iPad ? 20 : 21.5
let settings = ChartBarViewSettings(animDuration: 0.5)
let (p1, p2): (CGPoint, CGPoint) = {
return (CGPoint(x: chartPointModel.screenLoc.x, y: bottomLeft.y), CGPoint(x: chartPointModel.screenLoc.x, y: chartPointModel.screenLoc.y))
}()
let viewBar = ChartPointViewBar(p1: p1, p2: p2, width: barWidth, bgColor: #colorLiteral(red: 0.1725490196, green: 0.6509803922, blue: 1, alpha: 1), settings: settings)
print("viewBar frame", viewBar.frame)
let mGradient = CAGradientLayer()
mGradient.frame = viewBar.bounds
var colors = [CGColor]()
colors.append(UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor)
colors.append(UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor)
mGradient.startPoint = CGPoint(x: 0.1, y: 0.5)
mGradient.endPoint = CGPoint(x: 0.9, y: 0.5)
mGradient.colors = colors
viewBar.layer.addSublayer(mGradient)
viewBar.layer.masksToBounds = false
viewBar.layer.shadowOpacity = 0.2
viewBar.layer.shadowColor = #colorLiteral(red: 0.1725490196, green: 0.6509803922, blue: 1, alpha: 1).cgColor
viewBar.layer.shadowRadius = 4 // blur
viewBar.layer.shadowOffset = CGSize(width: 0, height: 2) // Spread
return viewBar
}
// create layer that uses viewGenerator to display chartpoints
let chartPointsLayer = ChartPointsViewsLayer(xAxis: xAxis.axis, yAxis: yAxis.axis, chartPoints: chartPoints, viewGenerator: barViewGenerator)
let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom
// create chart instance with frame and layers
let chart = Chart(view: self.chartView, innerFrame: innerFrame, settings: chartSettings, layers: [
coordsSpace.xAxisLayer,
coordsSpace.yAxisLayer,
guidelinesLayer,
chartPointsLayer
])
self.chart = chart
}
func rotated() {
for view in self.chartView.subviews {
view.removeFromSuperview()
}
self.initChart()
}
}
// MARK: - Actions
extension AnalysisController {
internal func updateMonthlyCost() {
APIService.sharedInstance.userMonthlySpendings { (cost) in
self.monthlyTotalView.setCostLabelValue(cost: cost)
}
}
}
// MARK: - Setup
extension AnalysisController {
internal func setupChartPosition() {
self.view.addSubview(self.backgroundView)
self.backgroundView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.backgroundView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
self.backgroundView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.backgroundView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.74).isActive = true
self.backgroundView.addSubview(self.chartView)
self.chartView.topAnchor.constraint(equalTo: self.backgroundView.topAnchor).isActive = true
self.chartView.bottomAnchor.constraint(equalTo: self.backgroundView.bottomAnchor, constant: -12).isActive = true
self.chartView.widthAnchor.constraint(equalTo: self.backgroundView.widthAnchor).isActive = true // 1
self.chartView.centerXAnchor.constraint(equalTo: self.backgroundView.centerXAnchor).isActive = true
}
internal func setupMonthlySpendingView() {
self.view.addSubview(self.monthlyTotalView)
self.monthlyTotalView.topAnchor.constraint(equalTo: self.backgroundView.bottomAnchor, constant: 15).isActive = true
self.monthlyTotalView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -7).isActive = true
self.monthlyTotalView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 7).isActive = true
self.monthlyTotalView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -60).isActive = true
self.updateMonthlyCost()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment