【SwiftUI】Chartsをまだ使ったことがなかったので株価アプリのUIっぽいの作って入門してみた1

# はじめに
iOS16から使えるようになったChartsをまだ使ったことがなかったので、iPhoneに標準で入ってる株価アプリのUIっぽい感じのものを作ってみました。
ほぼ同じのクローンみたいなの作ろうと思ったんですけど、難易度高くて諦めました。

# サンプルアプリ
今回作ったのはこんな感じ
![simulator_screenshot_F5254B35-F7E3-435F-BF3F-E81A558ED637.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/5e7ce94e-e0db-4e24-42d2-96833c937c89.png)

本当はタップした日時の株価が表示されたらいい感じだったんですけど、むずくて断念しました。
そのうちやります。

# 準備
今回、日付を扱うので軽くDate拡張を作成しました。
これはメインではないので適当な感じです。
```swift
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)!
    }
}
```

# モデルの用意
```swift
struct Stock: Identifiable {
    var id = UUID()
    var date: Date
    var value: Int
}
```

# 実装
```swift
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`で色を変えることができます。
![スクリーンショット 2023-12-17 22.56.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/a8033154-0105-d676-77f5-e61696db963d.png)
```diff_swift
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`で色を変えることができます。
![スクリーンショット 2023-12-17 22.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b7c9c0cc-783e-e889-ca21-70fbfe52e72d.png)

```diff_swift
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を配置することができます。
![スクリーンショット 2023-12-17 22.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/9f838946-7cb0-bbc9-f643-01873ed142d6.png)

```diff_swift
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にアニメーションとか付けてみたいですね