2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qiita全国学生対抗戦Advent Calendar 2023

Day 18

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

Posted at

はじめに

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

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

実際の株価アプリ

RPReplay_Final1702894460.gif

作成したサンプルアプリ

選択するとグラフが青くなり、選択している日付に縦線が表示されます。

Simulator Screen Recording - iPhone 15 Pro - 2023-12-18 at 15.18.03.gif

準備

前回のDate拡張と同様

モデルの用意

前回のモデルと同様

実装

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)
    }
}

解説

指が触れている日付を格納する変数を作成します。

// 選択している日付
@State private var selectedDate: Date?

以下の2つはセットです。
chartXSelectionで先ほど作った変数に日付を格納しています。
chart.selectXValue(at:)が実行されると、chartXSelectionが発火してselectedDateに値が入ります
指を離したら元に戻って欲しいのでonEndednilにします。

.chartXSelection(value: $selectedDate)
.chartGesture { chart in
    DragGesture(minimumDistance: 0)
        .onChanged {
            // ドラッグしている座標をチャートに知らせる
            chart.selectXValue(at: $0.location.x)
        }
        .onEnded { _ in
            // 指を離した時にselectedDateをリセット
            selectedDate = nil
        }
}

selectedDatenilじゃなければ、選択している日付に縦線を表示します。

if let selectedDate {
    // これが日付を選択した時に出てくる縦線
    RuleMark(
        x: .value("株価", selectedDate)
    )
    .foregroundStyle(chartColor)
}

おわり

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

参考記事

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?