如果你真的对画图很有兴趣,而且很想自己做图表的类别,那你可以使用程序在拿到资料後,用 UIView, CALayer 这些物件,画出你要的图案。但这边介绍 Android, iOS 双平台都知名的 Charts 套件,作者不同人,但 iOS Charts 的套件在 readme 上有写,iOS 的套件是参考 Android 的 API 写的,所以在说明文件的连结上,也直接指向 Android Charts 套件的说明文件。
iOS 套件的 GitHub 如下
https://github.com/danielgindi/Charts
Android 套件的 GitHub 如下
https://github.com/PhilJay/MPAndroidChart
说明文件
https://weeklycoding.com/mpandroidchart/
安装套件的方法
先更新 Podfile
# Uncomment the next line to define a global platform for your project
platform :ios, '14.0'
target 'ITIronMan' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for ITIronMan
pod 'Alamofire'
pod 'SwiftCSV'
pod 'Charts'
target 'ITIronManTests' do
inherit! :search_paths
# Pods for testing
end
end
然後用 terminal 进入专案资料夹,输入 pod install
等到跑完,Charts 就装好了。
独立开一个 KLineViewController,先放在 KLine.storyboard 里,并开出放 K Line Model 的 array,让 parent 传进来。目前 parent 暂定为下载台股加权开高低收的 VC,因为那个 VC 会有资料。
下方的蓝色区域,就是 Chart 图要放的位置
import UIKit
import Charts
class KLineViewController: UIViewController {
@IBOutlet weak var chartContainer: UIView!
var kLineDataSet = [StockKLine]()
// MARK: - life cycle
override func viewDidLoad() {
super.viewDidLoad()
}
}
在 TwStockMarketKLineViewController 加一个按钮,并让按钮发动转场,推入 KLineViewController
@IBAction func pushKLineButtonDidTap(_ sender: Any) {
let storyboard = UIStoryboard(name: "KLine", bundle: nil)
if let vc = storyboard.instantiateViewController(withIdentifier: "KLineViewController") as? KLineViewController {
vc.kLineDataSet = model.twExStockDataSet //将 K 线资料传进 KLineViewController
navigationController?.pushViewController(vc, animated: true)
}
}
在 viewDidLoad() 时,将 chartView 放进 container 里面,并把 autolayout 设定好。
整个 VC 的程序码如下
import UIKit
import Charts
class KLineViewController: UIViewController {
@IBOutlet weak var chartContainer: UIView!
private lazy var chartView: CandleStickChartView = {
let view = CandleStickChartView()
return view
}()
var kLineDataSet = [StockKLine]()
// MARK: - life cycle
override func viewDidLoad() {
super.viewDidLoad()
setupBasicUI()
setupCandleStickView()
}
// MARK: - private methods
private func setupBasicUI() {
chartContainer.backgroundColor = .clear
chartContainer.addSubview(chartView)
chartView.translatesAutoresizingMaskIntoConstraints = false
chartView.leadingAnchor.constraint(equalTo: chartContainer.leadingAnchor).isActive = true
chartView.topAnchor.constraint(equalTo: chartContainer.topAnchor).isActive = true
chartView.trailingAnchor.constraint(equalTo: chartContainer.trailingAnchor).isActive = true
chartView.bottomAnchor.constraint(equalTo: chartContainer.bottomAnchor).isActive = true
}
// 调整 Candle Stick View 的设定
private func setupCandleStickView() {
chartView.dragEnabled = false
chartView.setScaleEnabled(true)
chartView.maxVisibleCount = 200
chartView.pinchZoomEnabled = true
chartView.legend.horizontalAlignment = .right
chartView.legend.verticalAlignment = .top
chartView.legend.orientation = .vertical
chartView.legend.drawInside = false
chartView.legend.font = UIFont.systemFont(ofSize: 10)
chartView.leftAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.leftAxis.spaceTop = 0.3
chartView.leftAxis.spaceBottom = 0.3
chartView.leftAxis.axisMinimum = 0
chartView.rightAxis.enabled = false
chartView.xAxis.labelPosition = .bottom
chartView.xAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.maxVisibleCount = 20
}
}
接下来实作将 K 线的资料转换成 CandleChart 的部分。
在 Charts 套件里面,将 Data Model 转成 ChartView 可以用的 data 类别,会需要经过下列的转换。
step 1 → 将你的 Data Model 转换成对应的 DataEntry 类别
蜡烛图的类别为 CandleChartDataEntry
而他有下列的建构子
x: Chart View 的 位置,但要注意的是,CandleStickChart 的 x 不建议使用 timeIntervalSince1970,建议使用 Charts Demo 的 index。因为在 render 的时候, Candle Stick Chart 是有他自己的逻辑。如果用 timeInterval ,我个人测试的结果,Candle 的烛身会画不出来。
open: 开市价格
shadowH: 当日最高点
shadowL: 当日最低点
close: 当日收盘价
所以,需要一个转换 StockKLine 到 CandleChartDataEntry 的 func
private func convert(stockStick: [StockKLine]) -> [CandleChartDataEntry] {
var dataEntry = [CandleChartDataEntry]()
for (i, each) in stockStick.enumerated() {
let x = Double(i)
if let open = each.open,
let highest = each.highest,
let lowest = each.lowest,
let close = each.close {
let candleData = CandleChartDataEntry(x: x, shadowH: highest, shadowL: lowest, open: open, close: close)
dataEntry.append(candleData)
}
}
return dataEntry
}
step 2 → 将 DataEntry 类别转成 DataSet 类别
在 DataSet 是设定这一组数据画在 charts 上的设定,像是颜色、照哪一轴的比例。以
private func convert(dataEntry: [CandleChartDataEntry]) -> CandleChartDataSet {
let dataSet = CandleChartDataSet(entries: dataEntry)
dataSet.axisDependency = .left
dataSet.setColor(.red)
dataSet.drawIconsEnabled = false
dataSet.shadowColor = .darkGray
dataSet.shadowWidth = 0.5
dataSet.decreasingColor = .systemGreen //注意下跌的颜色在该地区的习惯
dataSet.decreasingFilled = true // 蜡烛线可以是实心,也可以是空心
dataSet.increasingColor = .systemRed //注意下跌的颜色在该地区的习惯
dataSet.increasingFilled = true
dataSet.neutralColor = .black //当开盘 == 收盘的时候颜色
return dataSet
}
step 3 → 将 DataSet 转换成 Data,ChartView 中的 data 会依照这样的 Data 画。
private func convert(dataSet: CandleChartDataSet) -> CandleChartData {
return CandleChartData(dataSet: dataSet)
}
将 1、2、3 步骤连起来,就完成了
private func update(_ chartView: CandleStickChartView, stockStickList: [StockKLine]) {
let dataEntry = convert(stockStick: stockStickList)
let dataSet = convert(dataEntry: dataEntry)
let data = convert(dataSet: dataSet)
chartView.data = data
}
整个 VC 的程序码如下
import UIKit
import Charts
class KLineViewController: UIViewController {
@IBOutlet weak var chartContainer: UIView!
private lazy var chartView: CandleStickChartView = {
let view = CandleStickChartView()
return view
}()
var kLineDataSet = [StockKLine]()
// MARK: - life cycle
override func viewDidLoad() {
super.viewDidLoad()
setupBasicUI()
setupCandleStickView()
update(self.chartView, stockStickList: kLineDataSet)
}
// MARK: - private methods
private func setupBasicUI() {
chartContainer.backgroundColor = .clear
chartContainer.addSubview(chartView)
chartView.translatesAutoresizingMaskIntoConstraints = false
chartView.leadingAnchor.constraint(equalTo: chartContainer.leadingAnchor).isActive = true
chartView.topAnchor.constraint(equalTo: chartContainer.topAnchor).isActive = true
chartView.trailingAnchor.constraint(equalTo: chartContainer.trailingAnchor).isActive = true
chartView.bottomAnchor.constraint(equalTo: chartContainer.bottomAnchor).isActive = true
}
private func setupCandleStickView() {
chartView.dragEnabled = false
chartView.setScaleEnabled(true)
chartView.maxVisibleCount = 200
chartView.pinchZoomEnabled = true
chartView.legend.enabled = false
chartView.leftAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.leftAxis.spaceTop = 0.3
chartView.leftAxis.spaceBottom = 0.3
chartView.leftAxis.axisMinimum = 0
chartView.rightAxis.enabled = false
chartView.xAxis.labelPosition = .bottom
chartView.xAxis.labelFont = UIFont.systemFont(ofSize: 10)
chartView.maxVisibleCount = 20
}
private func convert(stockStick: [StockKLine]) -> [CandleChartDataEntry] {
var dataEntry = [CandleChartDataEntry]()
for (i, each) in stockStick.enumerated() {
let x = Double(i)
if let open = each.open,
let highest = each.highest,
let lowest = each.lowest,
let close = each.close {
let candleData = CandleChartDataEntry(x: x, shadowH: highest, shadowL: lowest, open: open, close: close)
dataEntry.append(candleData)
}
}
return dataEntry
}
private func convert(dataEntry: [CandleChartDataEntry]) -> CandleChartDataSet {
let dataSet = CandleChartDataSet(entries: dataEntry)
dataSet.axisDependency = .left
dataSet.setColor(.red)
dataSet.drawIconsEnabled = false
dataSet.shadowColor = .darkGray
dataSet.shadowWidth = 0.5
dataSet.decreasingColor = .systemGreen
dataSet.decreasingFilled = true
dataSet.increasingColor = .systemRed
dataSet.increasingFilled = true
dataSet.neutralColor = .blue
return dataSet
}
private func convert(dataSet: CandleChartDataSet) -> CandleChartData {
return CandleChartData(dataSet: dataSet)
}
private func updateMaxMin(_ chartView: CandleStickChartView, dataSet: CandleChartDataSet) {
let max = dataSet.yMax
let min = dataSet.yMin
chartView.leftAxis.axisMaximum = max * 1.05
chartView.leftAxis.axisMinimum = min * 0.95
}
private func update(_ chartView: CandleStickChartView, stockStickList: [StockKLine]) {
let dataEntry = convert(stockStick: stockStickList)
let dataSet = convert(dataEntry: dataEntry)
let data = convert(dataSet: dataSet)
chartView.data = data
updateMaxMin(chartView, dataSet: dataSet)
}
}
完成上述步骤後,Candle Chart 成品如下
目前这张图,是没办法提供比较有效的资讯,最大原因,就是 x 轴并不是有效资讯。在第一步 Convert Data Entry 那边,我们要使用 index 来画 k 线,就丧失了日期这一讯息。
下一篇: 补上 x 轴遗失的资讯
下方是这次 D1 ~ D12 的完成品,可以下载来试
App Store - 台股申购日历
<<: 铁人赛 Day25 -- JavaScript 初体验(三) -- 建构子
>>: D24-(9/24)-统一(1216)-刚开始学习买股票时就一直持有的股票
这边简单介绍两者差异和选择: Parcelable: 效能比Serializable好,在记忆体开销...
目前导览项目页面愈来愈完整,相对有愈来愈多小细节需要留意,尤其是资料量变多时,许多浏览时伴随的滚动效...
获取明牌,并不一定就是赌徒心态;正确的观念是,应该是要先了解,人家何会推荐这只?是从基本面?消息面?...
Intro UVa 一颗星选集 UVa Online Judge (wiki) 为线上自动评断系统,...
从无到有开发 ML 专案到布署需要 6 至 12 个月不等,在尚未有具体产出的过程中,会有对内部及...