암호화폐 관련 앱을 제작하면서, 주식 앱이나 암호화폐 거래소에서 쉽게 접할 수 있는 CandleStick Chart를 직접 구현하게 되었는데, 이를 기록으로 남겨두려고 해요.
iOS 에서 차트를 그릴 수 있는 라이브러리들을 여러 가지 도전해봤는데, Charts 라이브러리는 Swift-iOS로 제공되는 라이브러리로 가장 쉽고 코드가 깔끔했던 것 같아요.
먼저 그리고자 했던 차트는 BarChart
와 CandleStickChart
였어요. 그럼 해당 View들을 살펴보도록 할게요.
import Foundation
import CoreGraphics
/// Chart that draws bars.
open class BarChartView: BarLineChartViewBase, BarChartDataProvider
해당 객체의 경우는 BarLineChartViewBase
, BarChartDataProvider
를 상속 및 채택하는 객체에요
그럼 먼저 BarLineChartViewBase
부터 알아보도록 할게요
/// Base-class of LineChart, BarChart, ScatterChart and CandleStickChart.
open class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChartDataProvider, NSUIGestureRecognizerDelegate
{
private var _pinchZoomEnabled = false
private var _doubleTapToZoomEnabled = true
private var _dragXEnabled = true
private var _dragYEnabled = true
private var _scaleXEnabled = true
private var _scaleYEnabled = true
BarLineChartViewBase
의 경우는 아래와 같이 X,Y 축을 통해서 차트를 그리는 것을 위한 정의가 되어 있어요
해당 객체의 프로퍼티들을 통해서 차트의 베이스가 되는 배경, 움직임 등을 수정할 수 있을 것 같네요
public protocol BarChartDataProvider: BarLineScatterCandleBubbleChartDataProvider
{
var barData: BarChartData? { get }
var isDrawBarShadowEnabled: Bool { get }
var isDrawValueAboveBarEnabled: Bool { get }
var isHighlightFullBarEnabled: Bool { get }
}
BarChart에서 하나의 Bar를 나타낼 때 필요한 Data 및 Bar의 UI 속성을 지정해주기 위한 속성 등을 제공하는 Protocol로 보이네요
BarLineScatterCandleBubbleChartDataProvider
를 상속하는 관계이며, 위에서 말한 것처럼 Provider를 붙이고 있는 모든 프로토콜들은 차트들의 데이터를 어떻게 제공할지를 설정할 수 있는 속성들을 가지고 있는 것 같아요.
그럼 여러 예시를 통해서 한 번 그려보도록 할게요. BarChartView의 경우는 간단하게 그려보죠
일단은 ChartView
의 속성부터 변경하도록 할게요
private func attribute() {
self.noDataText = "데이터가 없습니다."
self.noDataFont = .systemFont(ofSize: 20)
self.noDataTextColor = .lightGray
self.backgroundColor = .white
self.xAxis.setLabelCount(4, force: false)
self.xAxis.labelPosition = .bottom
self.dragDecelerationEnabled = false
self.autoScaleMinMaxEnabled = true
self.doubleTapToZoomEnabled = false
self.highlightPerTapEnabled = false
self.rightAxis.enabled = false
self.leftAxis.enabled = true
self.scaleYEnabled = false
self.dragYEnabled = false
}
네이밍이 직관적으로 되어 있어서 따로 설명은 하지 않아도 쉽게 알아볼 수 있을 것이라고 생각해요. 단순하게 View의 속성을 변경시키는 것이라 설명할 것이 없네요
private func setChart(chartData: [ChartData]) {
let dataEntries = self.convertToDataEntries(from: chartData)
let axisValues = self.convertToAxisValues(from: chartData)
let colors = self.barColors(with: chartData)
let chartDataSet = BarChartDataSet(entries: dataEntries).then {
$0.colors = colors
$0.drawValuesEnabled = false
$0.highlightEnabled = false
}
let chartData = CandleChartData(dataSet: chartDataSet)
self.data = chartData
self.xAxis.valueFormatter = IndexAxisValueFormatter(values: axisValues)
self.setVisibleXRangeMaximum(20.0)
self.moveViewToX(self.chartXMax)
self.drawMarkers = false
}
여기서부터는 조금씩 볼 내용이 있겠네요.
먼저, ChartData
의 경우는 Charts 라이브러리 내의 타입이 아니라 서버로부터 받아온 Entity를 Chart에서 사용하기 위해서 개인적으로 만든 모델 타입이라는 점!을 염두하시고 코드를 보면 좋겠네요.
BarChartView
에서 봉을 그리기 위해서는 우리는 먼저 BarChartDataEntry
가 있어야 해요. 그래서 아래처럼 ChartData
들을 통해서 Entry를 생성해줬어요
private func convertToDataEntries(from graphData: [ChartData]) -> [BarChartDataEntry] {
return graphData.enumerated().map {
BarChartDataEntry(x: Double($0),
y: $1.exchangeVolume)
}
}
x값은 단순하게 Index 이며, y값은 체결량을 의미해요
그리고 x축 데이터의 의미를 알려주기 위해서 AxisValue
인 String
을 생성해볼 거에요
private func convertToAxisValues(from graphData: [ChartData]) -> [String] {
return graphData.map { $0.dateText }
}
x값은 dateText
로 날짜를 가져왔어요
하지만, 이것만으로는 부족하죠. 각 Bar
들을 값을 통해서 어떤 정보인지 알 수 있지만 Bar
자체만으로도 정보를 담을 수 있기에 색을 넣어볼게요.
이 부분은 살짝 억지스러웠는데, 각 Bar
에 대한 색 정보를 모두 담아서 BarChartDataSet
에 넣어줬어요
private func barColors(with data: [ChartData]) -> [UIColor] {
return data.map {
if $0.openPrice > $0.closePrice {
return .systemBlue
} else if $0.openPrice < $0.closePrice {
return .systemRed
} else {
return .systemRed
}
}
}
이렇게 작성할 경우, 위 같은 결과가 나와요
디테일 한 부분까지는 손대지 않았지만, 정리하면 깔끔한 차트가 나오겠네요.
다음 시간에는 CandleStickChart
를 그려보도록 할게요