This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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