WWDC Quick Look 💓 By SwiftGGTeam
Swift Charts: Vectorized and function plots

Swift Charts: Vectorized and function plots

元の動画を見る

ハイライト

Swift Charts に関数プロットとベクトル化プロット API が追加され、前者は数学関数曲線を直接描画し、後者は大規模データセットを効率的にレンダリングします。


主要内容

Swift Charts は iOS 16 登場時、SwiftUI 的方式でデータチャートを描くことを目指しました。開発者は ForEach でデータを走査し、BarMark、PointMark などの Mark タイプで可視化を構築します。この API は柔軟で、各データ点を個別にスタイル設定できます。

実際のシーンでは、多くのチャートは統一されたビジュアルスタイルを持ちます——すべての点が同じシンボルサイズ、同じ色マッピング規則。ForEach で 1 点ずつ処理するのは「大げさ」な場合もあります。より一般的な課題は科学計算分野にあります——正規分布、パラメトリック方程式など、連続的な定義域を持つ数学関数は、離散的なデータ点では正確に表現できません。

今年の更新はこの 2 類のシーンを対象とします。LinePlot と AreaPlot は数学関数の描画用、ベクトル化 Plot API(PointPlot、RectanglePlot など)は大規模データセットの効率的処理用。両方向の設計思想は一致——「全体」を 1 つのエンティティとして扱い、要素ごとのループではない。


詳細

関数プロット

LinePlot と AreaPlot は新 API で、x を受け取り y を返す(または x、y のタプルを返す)クロージャを受け付けます。クロージャ内で任意の数学関数を呼び出せます:

Chart {
  LinePlot(
    x: "Capacity density", y: "Probability"
  ) { x in
    normalDistribution(
      x,
      mean: mean,
      standardDeviation: standardDeviation
    )
  }
}

キーポイント:

  • LinePlot は x と y ラベルを受け取り、クロージャのシグネチャは (Double) -> Double
  • normalDistribution は平均と標準偏差を事前計算したカスタム関数
  • Swift Charts が関数を自動サンプリングして曲線を生成

曲線下方の領域を塗りつぶすには AreaPlot を使います(03:57):

Chart {
  AreaPlot(
    x: "Capacity density", y: "Probability"
  ) { x in
    normalDistribution(x, ...)
  }
  .foregroundStyle(.gray)
  .opacity(0.2)
}

キーポイント:

  • AreaPlot は曲線下方の領域を塗りつぶす
  • foregroundStyle で塗りつぶし色を設定
  • opacity で透明度を調整し可読性を向上

AreaPlot は 2 本の曲線間の領域も描画可能。クロージャは yStart と yEnd を返します(04:21):

Chart {
  AreaPlot(
    x: "x", yStart: "cos(x)", yEnd: "sin(x)"
  ) { x in
    (yStart: cos(x / 180 * .pi),
     yEnd: sin(x / 180 * .pi))
  }
}

キーポイント:

  • クロージャは (yStart: Double, yEnd: Double) タプルを返す
  • 角度は弧度に変換:x / 180 * .pi
  • 2 関数間の差分や積分領域の可視化に使う

関数の定義域はデフォルトで Swift Charts が自動推論。手動指定も可能(04:59):

Chart {
  AreaPlot(...)
}
.chartXScale(domain: -315...225)
.chartYScale(domain: -5...5)

キーポイント:

  • .chartXScale.chartYScale で座標軸範囲を設定
  • 関数の特定部分を拡大/縮小

関数の一部だけ描画する場合、Plot 上でサンプリング域を指定(05:18):

Chart {
  AreaPlot(
    x: "x", yStart: "cos(x)", yEnd: "sin(x)",
    domain: -135...45
  ) { x in
    (yStart: cos(x / 180 * .pi),
     yEnd: sin(x / 180 * .pi))
  }
}

キーポイント:

  • domain パラメータで関数のサンプリング範囲を制限
  • .chartXScale とは別制御:前者はサンプリング、後者は表示範囲

Swift Charts はパラメトリック方程式をサポート。x と y を第 3 変数 t で表現(05:55):

Chart {
  LinePlot(
    x: "x", y: "y", t: "t", domain: -.pi ... .pi
  ) { t in
    let x = sqrt(2) * pow(sin(t), 3)
    let y = cos(t) * (2 - cos(t) - pow(cos(t), 2))
    return (x, y)
  }
}
.chartXScale(domain: -3...3)
.chartYScale(domain: -4...2)

キーポイント:

  • LinePlott パラメータと domain を追加
  • クロージャは t を受け取り (x, y) タプルを返す
  • ハート形曲線、螺旋線などのパラメトリック曲線に使用

区分関数で一部の入力が未定義の場合、.nan を返す(06:40):

Chart {
  LinePlot(x: "x", y: "1 / x") { x in
    guard x != 0 else {
      return .nan
    }
    return 1 / x
  }
}

キーポイント:

  • .nan はその x 値に対応する y 値がないことを示す
  • ゼロ除算、負数の対数、不正入力の処理に使う
  • Swift Charts はこれらの点をスキップし、線を途切れさせる

ベクトル化プロット

ベクトル化 Plot API は大規模データセット向け。従来方式は ForEach + Mark で各データ点を独立処理(07:43):

Chart {
  ForEach(model.data) {
    RectangleMark(
      x: .value("Longitude", $0.x),
      y: .value("Latitude", $0.y)
    )
    .foregroundStyle(by: .value("Axis type", $0.panelAxisType))
    .opacity($0.capacityDensity)
  }
}

キーポイント:

  • ForEach で各データ点を走査
  • 各 Mark が独立してスタイル属性を設定
  • 点ごとの差別化カスタマイズが必要なシーン向け

すべての点のスタイルが統一なら RectanglePlot に KeyPath でコレクション全体を一括処理(08:23):

Chart {
  RectanglePlot(
    model.data,
    x: .value("Longitude", \.x),
    y: .value("Latitude", \.y)
  )
  .foregroundStyle(by: .value("Axis type", \.panelAxisType))
  .opacity(\.capacityDensity)
}

キーポイント:

  • RectanglePlot はデータコレクション全体を受け取る
  • \.x\.y はストアドプロパティへの KeyPath
  • foregroundStyleopacity も KeyPath で一括設定

PointPlot も同様で、散布図に使う(09:42):

Chart {
  PointPlot(
    model.data,
    x: .value("Longitude", \.x),
    y: .value("Latitude", \.y)
  )
}

キーポイント:

  • PointPlot は散布図のベクトル化版
  • x と y パラメータはクロージャではなく KeyPath を受け取る

ベクトル化 Plot の modifier も KeyPath をサポート(10:26):

Chart {
  PointPlot(
    model.data,
    x: .value("Longitude", \.x),
    y: .value("Latitude", \.y)
  )
  .symbolSize(by: .value("Capacity", \.capacity))
  .foregroundStyle(by: .value("Axis type", \.panelAxisType))
}

キーポイント:

  • symbolSize は KeyPath で点のサイズにマッピング
  • foregroundStyle は KeyPath で色にマッピング
  • 走査なしでスタイルを一括適用

ベクトル化プロットではプロパティはストアドプロパティである必要があります。計算プロパティは呼び出しのたびに再計算が必要ですが、ストアドプロパティは固定メモリオフセットでアクセスできます(09:14)。

Swift Charts のアクセシビリティは関数プロットにデフォルトで適用されます。Audio Graph が関数曲線の音声説明を自動生成し、VoiceOver が座標軸とデータ系列情報を読み上げます(03:00)。


重要ポイント

1. データ比較に関数可視化を使う

データ分析でサンプルが特定の統計分布に従うと疑う場合、LinePlot で理論曲線を重ね、実分布と理論モデルの差を直感的に比較する。分布関数を定義し LinePlot を呼び出し、色や透明度で区別するだけ。

2. 大規模データセットにはベクトル化 API

散布図やヒートマップで 100 点を超える場合、ベクトル化 Plot API を優先。ForEach ループを PointPlot や RectanglePlot に置き換え、KeyPath でプロパティを指定——レンダリング効率が明確に向上。計算プロパティをストアドプロパティに変更すること。

3. 区分関数の未定義点は .nan で処理

定義域制限のある関数を描画するとき、未定義区間では .nan を返す。ゼロ除算、負数の対数、区分関数のギャップ——すべてこの方式で処理でき、Swift Charts が自動的に線を途切れさせる。


関連セッション

コメント

GitHub Issues · utterances