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

# はじめに
昨日、株価アプリの見た目を作成しました。
この記事は前回の記事から続いています。

https://qiita.com/SNQ-2001/items/3b92fceb72721262a907

今回は株価をタップ&ドラッグした時のアクションを追加してみます。

# 実際の株価アプリ
![RPReplay_Final1702894460.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/123885f4-83b9-6457-2ae0-83656fc83966.gif)

# 作成したサンプルアプリ
選択するとグラフが青くなり、選択している日付に縦線が表示されます。

![Simulator Screen Recording - iPhone 15 Pro - 2023-12-18 at 15.18.03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/82486c6e-2d89-30d6-3892-98f362e6bb59.gif)

# 準備
前回の[Date拡張](https://qiita.com/SNQ-2001/items/3b92fceb72721262a907#%E6%BA%96%E5%82%99)と同様

# モデルの用意
前回の[モデル](https://qiita.com/SNQ-2001/items/3b92fceb72721262a907#%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E7%94%A8%E6%84%8F)と同様

# 実装
```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),
    ]

    // 選択している日付
    @State private var selectedDate: Date?
    
    // チャートの色
    private var chartColor: Color {
        selectedDate != nil ? .cyan : .green
    }

    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: [chartColor.opacity(0.3), chartColor.opacity(0.1)]),
                    startPoint: .top,
                    endPoint: .bottom
                ))

                LineMark(
                    x: .value("日時", stock.date, unit: .day, calendar: .autoupdatingCurrent),
                    y: .value("株価", stock.value)
                )
                .foregroundStyle(chartColor)

                if let selectedDate {
                    // これが日付を選択した時に出てくる縦線
                    RuleMark(
                        x: .value("株価", selectedDate)
                    )
                    .foregroundStyle(chartColor)
                }
            }
        }
        .frame(height: 250)
        .chartXSelection(value: $selectedDate)
        .chartGesture { chart in
            DragGesture(minimumDistance: 0)
                .onChanged {
                    // ドラッグしている座標をチャートに知らせる
                    chart.selectXValue(at: $0.location.x)
                }
                .onEnded { _ in
                    // 指を離した時にselectedDateをリセット
                    selectedDate = nil
                }
        }
        .preferredColorScheme(.dark)
    }
}
```

# 解説
指が触れている日付を格納する変数を作成します。
```swift
// 選択している日付
@State private var selectedDate: Date?
```

以下の2つはセットです。
`chartXSelection`で先ほど作った変数に日付を格納しています。
`chart.selectXValue(at:)`が実行されると、`chartXSelection`が発火して`selectedDate`に値が入ります
指を離したら元に戻って欲しいので`onEnded`で`nil`にします。
```swift
.chartXSelection(value: $selectedDate)
.chartGesture { chart in
    DragGesture(minimumDistance: 0)
        .onChanged {
            // ドラッグしている座標をチャートに知らせる
            chart.selectXValue(at: $0.location.x)
        }
        .onEnded { _ in
            // 指を離した時にselectedDateをリセット
            selectedDate = nil
        }
}
```

`selectedDate`が`nil`じゃなければ、選択している日付に縦線を表示します。
```swift
if let selectedDate {
    // これが日付を選択した時に出てくる縦線
    RuleMark(
        x: .value("株価", selectedDate)
    )
    .foregroundStyle(chartColor)
}
```

# おわり
Charts用のモディファイアがいっぱいですね。

# 参考記事
https://swiftwithmajid.com/2023/07/18/mastering-charts-in-swiftui-selection/