はじめに
iOS16から使えるようになったChartsをまだ使ったことがなかったので、iPhoneに標準で入ってる株価アプリのUIっぽい感じのものを作ってみました。
ほぼ同じのクローンみたいなの作ろうと思ったんですけど、難易度高くて諦めました。
サンプルアプリ
本当はタップした日時の株価が表示されたらいい感じだったんですけど、むずくて断念しました。
そのうちやります。
準備
今回、日付を扱うので軽くDate拡張を作成しました。
これはメインではないので適当な感じです。
extension Date {
init(year: Int, month: Int, day: Int) {
let calendar = Calendar(identifier: .gregorian)
var components = DateComponents()
components.year = year
components.month = month
components.day = day
self = calendar.date(from: components)!
}
}
モデルの用意
struct Stock: Identifiable {
var id = UUID()
var date: Date
var value: Int
}
実装
import Charts
import SwiftUI
struct ContentView: View {
@State private var stocks: [Stock] = [
.init(date: .init(year: 2023, month: 10, day: 2), value: 2130),
.init(date: .init(year: 2023, month: 10, day: 3), value: 4160),
.init(date: .init(year: 2023, month: 10, day: 4), value: 4308),
.init(date: .init(year: 2023, month: 10, day: 5), value: 5220),
.init(date: .init(year: 2023, month: 10, day: 6), value: 6311),
.init(date: .init(year: 2023, month: 10, day: 10), value: 6311),
.init(date: .init(year: 2023, month: 10, day: 11), value: 5809),
.init(date: .init(year: 2023, month: 10, day: 12), value: 5350),
.init(date: .init(year: 2023, month: 10, day: 13), value: 5196),
.init(date: .init(year: 2023, month: 10, day: 16), value: 5200),
.init(date: .init(year: 2023, month: 10, day: 17), value: 6123),
.init(date: .init(year: 2023, month: 10, day: 18), value: 6110),
.init(date: .init(year: 2023, month: 10, day: 19), value: 6092),
.init(date: .init(year: 2023, month: 10, day: 20), value: 6501),
.init(date: .init(year: 2023, month: 10, day: 23), value: 6867),
.init(date: .init(year: 2023, month: 10, day: 24), value: 7381),
.init(date: .init(year: 2023, month: 10, day: 25), value: 7980),
.init(date: .init(year: 2023, month: 10, day: 26), value: 9016),
.init(date: .init(year: 2023, month: 10, day: 27), value: 8221),
.init(date: .init(year: 2023, month: 10, day: 30), value: 7217),
.init(date: .init(year: 2023, month: 10, day: 31), value: 6221),
]
var body: some View {
Chart {
ForEach(stocks, id: \.id) { stock in
AreaMark(
x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
y: .value("株価", stock.value)
)
.foregroundStyle(.linearGradient(
.init(colors: [.green.opacity(0.3), .green.opacity(0.1)]),
startPoint: .top,
endPoint: .bottom
))
LineMark(
x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
y: .value("株価", stock.value)
)
.foregroundStyle(.green)
.symbol(.circle)
}
}
.frame(height: 250)
.preferredColorScheme(.dark)
}
}
解説
折れ線グラフはLineMark
です。
foregroundStyle
で色を変えることができます。
Chart {
ForEach(stocks, id: \.id) { stock in
+ LineMark(
+ x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
+ y: .value("株価", stock.value)
+ )
.foregroundStyle(.green)
}
}
下に向かってグラデーションがかかってるやつはAreaMark
です。
LineMark
とAreaMark
を組み合わせて使うことで折れ線グラフの下を塗りつぶすことができます。
こちらもforegroundStyle
で色を変えることができます。
Chart {
ForEach(stocks, id: \.id) { stock in
+ AreaMark(
+ x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
+ y: .value("株価", stock.value)
+ )
.foregroundStyle(.linearGradient(
.init(colors: [.green.opacity(0.3), .green.opacity(0.1)]),
startPoint: .top,
endPoint: .bottom
))
}
}
symbol
を使用することによって、折れ線グラフの値のある位置にViewを配置することができます。
Chart {
ForEach(stocks, id: \.id) { stock in
LineMark(
x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
y: .value("株価", stock.value)
)
.foregroundStyle(.green)
+ .symbol(.circle)
}
}
おわり
次回はChartsにアニメーションとか付けてみたいですね