SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

iOS16の新機能に触れてみよう

iOS16で追加されたChartsフレームワークで日々の売上データをグラフ化してみる

iOS16の新機能に触れてみよう 第4回

  • X ポスト
  • このエントリーをはてなブックマークに追加

Chartsフレームワークの実践的な使用例

 Chartsフレームワークをさらに使いこなすために、よく利用されるY軸領域の調整とチャートの描画時のアニメーションとX軸の期間を変更する例を挙げます。それぞれについて前節で作成したサンプルに機能を追加してみます。

グラフを見やすくする

 既定のままグラフを表示すると、前節のサンプルのようにチャートのY軸の上部が詰まったように少し窮屈な感じで表示されます。そこで、Y軸に少し余裕を持たせて表示してみます。同時に棒グラフの色もグラデーションにしてみます。

[リスト3]ChartsSample/ContentView.swift抜粋
func SampleChart() -> some View {
  // サンプルデータ内のamount の最大値を取得
  let max = sampleData.max(by: { $1.amount > $0.amount })?.amount ?? 0 // -------- ①
        
    Chart {
        ForEach(sampleData){ item in
            BarMark(    // 棒グラフを表示
                x: .value("date", item.date, unit: .day),
                y: .value("amount", item.amount)
            ).foregroundStyle(Color.blue.gradient)  // -------- ③
        }
    }
    .frame(height: 400)
    .chartYScale(domain: 0...(max + 5000)) // -------- ②
}
棒グラフの表示
棒グラフの表示

 チャートを表示する前に、サンプルデータ内のY軸の最大値を取得しておきます(①)。サンプルデータは配列なので、maxメソッドでamountの最も大きいものを取得します。maxメソッドで取得できない場合も考慮してオプショナル型で取得し、取得できない場合の最大値は0としています。

 取得した最大値に5000を加算した値を、chartYScaleメソッドでY軸の範囲の最大に指定します(②)。この処理で、Y軸の最大値をmax+5000としたチャートを表示できます。棒グラフの色は、foregroundStyleメソッドで指定します(③)。foregroundStyleメソッドに関しては、本連載第2回で確認できます。

アニメーション効果を実装する

 グラフを表示するときに、アニメーション効果をつけることも可能です。作成したサンプルにアニメーションを実装してみます。最初に次のようにチャートで表示する構造体にアニメーション用のプロパティを設けます。

[リスト5]StockChartSample/Model.swift抜粋
struct DailyAmount: Identifiable {
    var id = UUID()
    var date: Date
    var amount: Double
    var animate: Bool = false  // アニメーション用のプロパティ
}

 サンプルでは、Bool型のanimateプロパティを定義しました。次に、animateプロパティがfalseからtrueに変わるときにアニメーションを行えるようにshowAnimateのメソッド名で処理を定義します。

[リスト6]StockChartSample/ContentView.swift抜粋
func showAnimate() {
    // animateプロパティをfalseに
    sampleData = sampleData.map({ DailyAmount(date: $0.date, amount: $0.amount, animate: false) })   // -------- ①
    
    for (index,_) in sampleData.enumerated(){   // -------- ②
        // 各データを index * 0.05秒遅延して処理を行う
        DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.05){
            // 順番にeaseInOutアニメーションを0.5秒かけて実行
            withAnimation(.easeInOut(duration: 0.5)){
                sampleData[index].animate = true
            }
        }
    }
}

 メソッドの最初では、animateプロパティをfalseにしておきます(①)。その後で、チャートに表示するsampleData内の要素である各DailyAmount内のanimateプロパティを、順番に時間差でtrueにしていきます(②)。これによって、チャートが左から順にアニメーションしていく効果を実装できます。

 作成したshowAnimate()メソッドは、チャートを表示、または折れ線グラフに切り替えるタイミングで実行できるようにします。

[リスト7]StockChartSample/ContentView.swift抜粋
var body: some View {
    NavigationStack {
        VStack {
            VStack(alignment: .leading, spacing: 8) {
                Toggle("Line Graph", isOn: $showLineGraph)
                    .padding(.top)
                    .onChange(of: showLineGraph) { newValue in // -------- ②
                        showAnimate()    // アニメーション実行
                    }
                SampleChart()
# 略
    }
    .onAppear {    // -------- ①
        showAnimate()    // アニメーション実行
    }
}

 NavigationStackの表示時(①)、Toggleの切り替え時(②)にshowAnimate()メソッドを実行します。最後に、チャートを表示するときに、animetaプロパティがtrueであればamoutプロパティの値/falseであれば0を表示するようにします(③)。

[リスト8]StockChartSample/ContentView.swift抜粋
Chart {
    ForEach(sampleData){ item in
        if showLineGraph {
            LineMark(
                x: .value("date", item.date, unit: .day),
                // animetaプロパティがtrueであればamoutプロパティの値/falseであれば0を表示
                y: .value("amount", item.animate ? item.amount : 0)    // -------- ③
            )
            .foregroundStyle(Color.blue)
            
            AreaMark(
                x: .value("date", item.date ,unit: .day),
                y: .value("amount", item.animate ? item.amount : 0)   // -------- ③
            )
            .foregroundStyle(Color.blue).opacity(0.1)
        } else {
            BarMark(
                x: .value("date", item.date, unit: .day),
                y: .value("amount", item.animate ? item.amount : 0)   // -------- ③
            ).foregroundStyle(Color.blue.gradient)
        }

 サンプルを実行すると、チャートを描画する際にアニメーションが適用されることを確認できます。

棒グラフのアニメーション
棒グラフのアニメーション
折れ線グラフのアニメーション
折れ線グラフのアニメーション

 アニメーションの動きを変更したい場合には、showAnimateメソッド内のanimeteプロパティの値をtrueにするタイミング(間隔)を調整してみましょう。

週ごとに集計してチャートを表示する

 日単位で表示していたチャートを週単位や月単位に切り替えて表示することも、Swiftのコレクションを扱う機能を利用して比較的短いコードで実装できます。最初に週単位のデータを定義する構造体を設けます。

[リスト9]StockChartSample/Models.swift抜粋
struct WeeklyAmount: Identifiable {
    var id = UUID()
    var index: Int                      // 第何週というインデックス
    var amount: Double           // 週単位のamount
    var animate: Bool = false   // アニメーション用のプロパティ
}

 次に、日単位(days)と週単位(week)を区別するためのPickerを設けます。ピッカーで週単位が選択された場合に、sampleDataを週単位のsampleDataWeeklyに分ける処理を行います。

[リスト10]StockChartSample/ContentView.swift抜粋
struct ContentView: View {
# 略
    @State var sampleDataWeekly: [WeeklyAmount] = []    // 週単位の構造体の配列
    @State var selectedTab: String = "days"    // 日単位か週単位かの選択
    var isWeek: Bool { get {  eturn selectedTab == "week" } }    // 週単位の選択か?
# 略
        Picker("", selection: $selectedTab) {   // 日単位/週単位の選択ピッカー
            Text("days").tag("days")
            Text("week").tag("week")
        }
        .pickerStyle(.segmented)
        .onChange(of: selectedTab) { newValue in    // -------- ①
            if isWeek {
                // 日単位のデータを第○週という単位で辞書に分割
                let calendar = Calendar(identifier: .gregorian)
                let dic = Dictionary(grouping: sampleData, by: { calendar.component(.weekOfMonth, from: $0.date) }).sorted(by: { $0.0 < $1.0 })
                // 週単位のWeeklyAmount構造体の配列にまとめる
                sampleDataWeekly = dic.map({ (key,value) in
                    return WeeklyAmount(index: key, amount: value.map{ $0.amount }.reduce(0, +))
                })
             }
            showAnimate()
# 略

 ピッカーで週単位が選択された場合には、データを週単位で分割したあとに集計し直しています。配列をクロージャで指定した条件で分割する処理については、「Swift 4で最初に知っておきたい3つのポイント」連載の第2回で確認できます。

 グラフ表示やアニメーションの処理は、sampleDataに対して行った処理をsampleDataWeeklyに対しても行う内容なのでコードは割愛します。

週単位のチャート
週単位のチャート

 Chartで表示するデータは配列や辞書といったコレクションで用意することが多いです。コレクションの加工が容易というSwiftの特徴を利用することで、違う期間でのグラフの表示など、データをグループ化したチャートの切り替えも比較的容易に行うことができます。

まとめ

 今回はSwiftUI 4で追加Swift Chartsフレームワークの使い方の例について説明しました。次回は、WeatherKitについて説明します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
iOS16の新機能に触れてみよう連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 片渕 彼富(カタフチ カノトミ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17200 2023/02/08 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング