View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。

  • 概要
  • トランスクリプト
  • コード
  • Swift Chartsの円グラフとインタラクティブ性の詳細

    Swift Chartsは円を描きます。フレームワークへの最新の改善で、アプリで円グラフやドーナツグラフを作成する準備をしましょう。グラフをスクロール可能にする方法を学び、データの追加的な詳細を明らかにするためのグラフ選択APIを探索し、追加的なインタラクティブ性を有効にすることで、グラフをさらに楽しくする方法を見つけましょう。

    関連する章

    • 0:20 - Pie charts
    • 4:22 - Selection
    • 7:49 - Scrolling

    リソース

    • Visualizing your app’s data
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC23

    • 進化したScrollViewの詳細
    • SwiftUIの新機能
  • ダウンロード

    ♪ ♪

    こんにちは Richardです Swift Chartsの エキサイティングな新機能について お話しできることを嬉しく思います 円グラフ 選択機能 そしてスクロールです 円グラフから始めましょう Swift Chartsはあらゆる種類の データ視覚化を作成するための 構成可能でカスタマイズ可能な 構成要素を提供します Swift Chartsシリーズに新しく 美しい円グラフが追加されました 円グラフは合計値がさまざまなカテゴリに どのように構成されるかを シンプルで見慣れた図形で示します これは友人のフードトラックの パンケーキの販売データ を視覚化したグラフです 円グラフには軸がないため 精度が重要ではない カジュアルな表示に最適です どのように完全な円を 構成するか広く理解すると 部分と全体の関係を 直感的に視覚化できます 円グラフが好まれる重要な理由の1つは 丸くて親しみやすい形状です すでによく知っているマークベースの composition syntaxを使用して 円グラフを作成できます 新しいマークタイプ SectorMarkの導入です SectorMarkは パイのスライスを表します それは極空間に位置しています この極ではなく極座標系です Sectorのサイズは Sectorが表す値に比例します 半径に沿って Sectorの外観をカスタマイズできます 内側の半径を大きくすると 円グラフはドーナツグラフになります SectorMarkを使用すると あらゆる種類のグラフを簡単に作成できます 例を示しましょう 私の友人のパンケーキ フードトラックビジネスでは 日々の売上が大きく活性化して成長しました 昨年は6種類のパンケーキを販売しました この夏私は彼らの販売アプリの改善を 支援するという新たな課題に取り組みました

    最も売れているスタイルのパンケーキを 視覚化したグラフから始めます アプリには単純な積み上げ棒グラフ が用意されています これにはBarMarkが付けられ 売上はX次元に沿って積み上げられます カテゴリデータであるため カテゴリは各積み上げ棒の フォアグラウンドスタイル として反映されます このグラフで十分ですが 画面上の空きスペースを活用するために 円グラフに変換しましょう データを目立たせることができます BarMarkと引数xを SectorMarkとangleに 切り替えるだけです とても簡単です!

    SectorMarkでは販売数量を 表すために角度を使用します 円グラフに指定した角度の値は 完全な円に自動的に正規化されます スタイルのカスタマイズを 適用することもできます angularInsetをSectorに設定して Sector間にギャップを作成できます ここではSectorの角度インセットを 1.5 ポイントに設定しているため 2つのSector間のギャップは インセットの2倍で幅は3ポイントです 角の半径も設定して 円周がきれいに丸みを帯びるようにしました わずか数行のコードだけで 素晴らしいものになります 別の外観を試すために これをドーナツグラフに変えましょう

    内側の半径を 円の全半径の比率に設定しました 私にとって黄金比は ちょうどいいように見えますが ドーナツの厚さは 別の方が好みかもしれません 販売されたすべてのパンケーキの中で 現在チャート上に表示されているのは 断然の勝者cachapaです しかしドーナツグラフは 中央が空洞になっているため テキストをグラフの中央に 移動したいと考えています chartBackgroundに テキストを配置します テキストがドーナツの穴の 中央に配置されるように いくつかの位置計算を使用します

    これで見栄えの良い ドーナツグラフが完成しました これが円グラフとドーナツグラフです データに印象を与えるのに 最適な方法であり 大画面で見ると非常に美しいです 次に「選択」による グラフの双方向機能について 詳しく説明したいと思います グラフでインタラクティブ性を 有効にすることで 追加の詳細を 徐々に開示することになります インタラクティブ性によりユーザーは タッチなどのさまざまな形式の入力で 自然にデータを探索することができます

    「選択」はチャートと 直接通信する方法です 心拍数グラフなどの Appleが設計したグラフは その完璧な例です 軸に沿って点を選択すると チャートに追加情報が表示されます このアイデアをパンケーキ販売アプリに 取り入れてみましょう

    アプリ内の1つのグラフは 2つの都市の曜日ごとの 平均売上高を視覚化します

    このチャートで値の選択を有効にして 選択日に販売されたパンケーキの数を示す ポップオーバーによって 詳細な販売数を表示します

    このようにグラフが定義されます 各都市にはデータシリーズがあります シリーズの各要素には 曜日と販売数があります 次に線は都市名によって スタイル設定されます chartOverlay modifierについては すでにご存知かもしれません このmodifierでSwiftUIビューを オーバーレイしてジェスチャをキャプチャします iOS17では新しいchartXSelection modifierを使用できます すべてのジェスチャ認識を処理し 選択した値をバインディングに保存します

    選択modifierにより X軸に沿った生の日付値が得られるので 私の折れ線グラフでは それをデータポイントと照合する 計算プロパティを定義します グラフを拡張して 値が選択されたときに ポップオーバーを表示しましょう

    値を選択すると 選択インジケーターとして 縦罫マークを追加します ルールマークがラインマークの 後ろに留まるように Zインデックスをデフォルトの 0値よりも低く設定しました 次に選択インジケーターの上に ポップオーバーを作成しましょう これはカスタムSwiftUIビューを 使用して注釈として行うことができます 通常注釈はチャート内に配置されますが この場合ポップオーバーは グラフの境界を越えて広がります ここで注釈のオーバーフロー解決が 必要になります

    X軸ではポップオーバーが グラフの水平方向の 境界を越えないよう グラフにフィットさせます Y軸では注釈が グラフのすぐ上に来るように オーバーフロー解決を無効にします

    選択バインディングと 注釈付きのルールマークを使用すると これで対話型の 折れ線グラフが作成されました Swift ChartsはmacOSでの選択も サポートしており 値選択のデフォルトは ホバージェスチャです

    単一の値の選択に加えて このチャート選択modifierの バリアントでは範囲を選択できます iOSではデフォルトは2本指の タップジェスチャです macOSではこれは ドラッグジェスチャです Swift ChartsではChartProxyを使い ジェスチャの位置に基づいて 値を選択できるよう選択用の カスタムジェスチャを提供できます

    X座標とY座標のグラフ以外にも グラフ値の選択は円グラフや ドーナツグラフでもシームレスに機能します Sectorをタップして ハイライト表示するのはとても楽しいです

    選択とはチャート内の追加情報を 明らかにすることがすべてでした インタラクティブ性のもう1つの 重要な部分はデータのナビゲーションです スクロールについて話しましょう

    1年間の毎日のパンケーキの売上を 視覚化するグラフを作成したいと思います 365日すべてを画面に 収めることは非現実的なので スクロール可能にする必要があります それにはchartScrollableAxes modifierを呼び出します chartXVisibleDomain を使用して時間間隔として 30日間の表示ウィンドウを設定します 現在表示されているドメインで パンケーキの総売上を表示するには chartScrollPositionを使用して 現在の日付をバインディングに保存します これで指を使って スクロールできるようになりました

    プロットをスクロールできるだけでなく 軸のコンテンツも一緒にスクロールでき 非常にスムーズです スクロールはいくつかの 方法でカスタマイズできます たとえばスクロールを常に 日付単位にスナップしたいとします これによりスクロール動作が始まります ScrollTargetBehaviorは SwiftUIおよびSwift Charts に新しく追加され スクロールビュー のコンテンツを値に揃えます

    私が望んでいたスナップ動作については 1日の最初の時間に合わせて設定しました majorAlignmentは スワイプ動作を定義することで さらに一歩カスタマイズします ここでは月の最初の日に設定することで スワイプしてチャートのページを移動しても いつも毎月1日に戻っています

    スクロール可能なグラフは 最新の機能強化の一部 に基づいて構築され SwiftUIでビューをスクロールします 詳細については「Beyond ScrollViews」を 必ずご確認ください Swift Chartはデータを視覚化する 無限の可能性を提供します X座標とY座標のグラフを超えて 円グラフもAPIシリーズの一部になり Appleが設計したグラフを作成します 円グラフはシンプルかつ 強力な視覚化です データの一部と全体の関係を表す 場合に最も効果的です 選択やスクロールなどの 双方向機能により データの視覚化に まったく新しい次元が可能になります ユーザーは運転席に座って データを探索できます パイやドーナツのグラフを おいしく味わってください ♪ ♪

    • 2:06 - Stacked bar chart

      Chart(data, id: \.name) { element in
        BarMark(
          x: .value("Sales", element.sales),
          stacking: .normalized
        )
        .foregroundStyle(by: .value("Name", element.name))
      }
      .chartXAxis(.hidden)
    • 2:44 - Pie chart

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales)
        )
        .foregroundStyle(by: .value("Name", element.name))
      }
    • 3:05 - Pie chart with angular inset

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales),
          angularInset: 1.5
        )
        .foregroundStyle(by: .value("Name", element.name))
      }
    • 3:06 - Pie chart with corner radius

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales),
          angularInset: 1.5
        )
        .cornerRadius(5)
        .foregroundStyle(by: .value("Name", element.name))
      }
    • 3:33 - Donut chart

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales),
          innerRadius: .ratio(0.618),
          angularInset: 1.5
        )
        .cornerRadius(5)
        .foregroundStyle(by: .value("Name", element.name))
      }
    • 4:02 - Donut chart with text in the center

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales),
          innerRadius: .ratio(0.618),
          angularInset: 1.5
        )
        .cornerRadius(5)
        .foregroundStyle(by: .value("Name", element.name))
      }
      .chartBackground { chartProxy in
        GeometryReader { geometry in
          let frame = geometry[chartProxy.plotAreaFrame]
          VStack {
            Text("Most Sold Style")
              .font(.callout)
              .foregroundStyle(.secondary)
            Text(mostSold)
              .font(.title2.bold())
              .foregroundColor(.primary)
          }
          .position(x: frame.midX, y: frame.midY)
        }
      }
    • 5:14 - Chart visualizing average sales by city

      struct LocationDetailsChart: View {
        ...
      
        var body: some View {
          Chart {
            ForEach(data) { series in
              ForEach(series.sales, id: \.day) { element in
                LineMark(
                  x: .value("Day", element.day, unit: .day),
                  y: .value("Sales", element.sales)
                )
              }
              .foregroundStyle(by: .value("City", series.city))
              .symbol(by: .value("City", series.city))
              .interpolationMethod(.catmullRom)
            }
          }
          ...
        }
      }
    • 5:39 - Chart selection modifier

      struct LocationDetailsChart: View {
        @Binding var rawSelectedDate: Date?
      
        var body: some View {
          Chart {
            ForEach(data) { series in
              ForEach(series.sales, id: \.day) { element in
                LineMark(
                  x: .value("Day", element.day, unit: .day),
                  y: .value("Sales", element.sales)
                )
              }
              .foregroundStyle(by: .value("City", series.city))
              .symbol(by: .value("City", series.city))
              .interpolationMethod(.catmullRom)
            }
          }
          .chartXSelection(value: $rawSelectedDate)
        }
      }
    • 5:47 - Processing raw selected date from chart selection binding

      struct LocationDetailsChart: View {
        @Binding var rawSelectedDate: Date?
      
        var selectedDate: Date? {
          guard let rawSelectedDate else { return nil }
          return data.first?.sales.first(where: {
            let endOfDay = endOfDay(for: $0.day)
            return ($0.day ... endOfDay).contains(rawSelectedDate)
          })?.day
        }
      
        var body: some View {
          Chart {
            ForEach(data) { series in
              ForEach(series.sales, id: \.day) { element in
                LineMark(
                  x: .value("Day", element.day, unit: .day),
                  y: .value("Sales", element.sales)
                )
              }
              .foregroundStyle(by: .value("City", series.city))
              .symbol(by: .value("City", series.city))
              .interpolationMethod(.catmullRom)
            }
          }
          .chartXSelection(value: $rawSelectedDate)
        }
      }
    • 6:06 - Rule mark as selection indicator

      Chart {
        ForEach(data) { series in
          ForEach(series.sales, id: \.day) { element in
            LineMark(
              x: .value("Day", element.day, unit: .day),
              y: .value("Sales", element.sales)
            )
          }
        }
        if let selectedDate {
          RuleMark(
            x: .value("Selected", selectedDate, unit: .day)
          )
          .foregroundStyle(Color.gray.opacity(0.3))
          .offset(yStart: -10)
          .zIndex(-1)
        }
      }
      .chartXSelection(value: $rawSelectedDate)
    • 6:20 - Selection popover

      Chart {
        ForEach(data) { series in
          ForEach(series.sales, id: \.day) { element in
            LineMark(
              x: .value("Day", element.day, unit: .day),
              y: .value("Sales", element.sales)
            )
          }
        }
        if let selectedDate {
          RuleMark(
            x: .value("Selected", selectedDate, unit: .day)
          )
          .foregroundStyle(Color.gray.opacity(0.3))
          .offset(yStart: -10)
          .zIndex(-1)
          .annotation(
            position: .top, spacing: 0,
            overflowResolution: .init(
              x: .fit(to: .chart),
              y: .disabled
            )
          ) {
            valueSelectionPopover
          }
        }
      }
      .chartXSelection(value: $rawSelectedDate)
    • 7:07 - Range selection

      Chart(data) { series in
        ForEach(series.sales, id: \.day) { element in
          LineMark(
            x: .value("Day", element.day, unit: .day),
            y: .value("Sales", element.sales)
          )
        }
        ...
      }
      .chartXSelection(value: $rawSelectedDate)
      .chartXSelection(range: $rawSelectedRange)
    • 7:22 - Overriding default selection gesture

      Chart(data) { series in
        ForEach(series.sales, id: \.day) { element in
          LineMark(
            x: .value("Day", element.day, unit: .day),
            y: .value("Sales", element.sales)
          )
        }
        ...
      }
      .chartXSelection(value: $rawSelectedDate)
      .chartGesture { proxy in
        DragGesture(minimumDistance: 0)
          .onChanged { proxy.selectXValue(at: $0.location.x) }
          .onEnded { _ in selectedDate = nil }
      }
    • 7:31 - Selection in pie charts and donut charts

      Chart(data, id: \.name) { element in
        SectorMark(
          angle: .value("Sales", element.sales),
          innerRadius: .ratio(0.618),
          angularInset: 1.5
        )
        .cornerRadius(5)
        .foregroundStyle(by: .value("Name", element.name))
        .opacity(element.name == selectedName ? 1.0 : 0.3)
      }
      .chartAngleSelection(value: $selectedAngle)
    • 7:54 - Daily sales chart

      Chart {
        ForEach(SalesData.last365Days, id: \.day) {
          BarMark(
            x: .value("Day", $0.day, unit: .day),
            y: .value("Sales", $0.sales)
          )
        }
        .foregroundStyle(.blue)
      }
    • 8:07 - Daily sales chart with a scrollable axis

      Chart {
        ForEach(SalesData.last365Days, id: \.day) {
          BarMark(
            x: .value("Day", $0.day, unit: .day),
            y: .value("Sales", $0.sales)
          )
        }
        .foregroundStyle(.blue)
      }
      .chartScrollableAxes(.horizontal)
    • 8:11 - Setting the visible domain for a scrollable chart

      Chart {
        ForEach(SalesData.last365Days, id: \.day) {
          BarMark(
            x: .value("Day", $0.day, unit: .day),
            y: .value("Sales", $0.sales)
          )
        }
        .foregroundStyle(.blue)
      }
      .chartScrollableAxes(.horizontal)
      .chartXVisibleDomain(length: 3600 * 24 * 30)
    • 8:18 - Chart scroll position

      Chart {
        ForEach(SalesData.last365Days, id: \.day) {
          BarMark(
            x: .value("Day", $0.day, unit: .day),
            y: .value("Sales", $0.sales)
          )
        }
        .foregroundStyle(.blue)
      }
      .chartScrollableAxes(.horizontal)
      .chartXVisibleDomain(length: 3600 * 24 * 30)
      .chartScrollPosition(x: $scrollPosition)
    • 8:50 - Snapping in a scrolling chart

      Chart {
        ForEach(SalesData.last365Days, id: \.day) {
          BarMark(
            x: .value("Day", $0.day, unit: .day),
            y: .value("Sales", $0.sales)
          )
        }
        .foregroundStyle(.blue)
      }
      .chartScrollableAxes(.horizontal)
      .chartXVisibleDomain(length: 3600 * 24 * 30)
      .chartScrollPosition(x: $scrollPosition)
      .chartScrollTargetBehavior(
        .valueAligned(
          matching: DateComponents(hour: 0),
          majorAlignment: .matching(DateComponents(day: 1))))
  • 特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。

    クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。

Developer Footer

  • ビデオ
  • WWDC23
  • Swift Chartsの円グラフとインタラクティブ性の詳細
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン