View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

返回 WWDC25

大多数浏览器和
Developer App 均支持流媒体播放。

  • 简介
  • 转写文稿
  • 代码
  • 将 Swift Charts 扩展成三维图表

    了解如何使用 Chart3D 将你的 2D Swift Charts 转换为三维图表,从全新角度直观呈现你的数据集。绘制 3D 数据图表,以可视化方式显示数学表面,并对摄像头、材质等各种因素进行自定,让你的 3D 图表更加清晰直观、赏心悦目。 为了充分从这个讲座中获益,我们建议你先熟悉一下如何创建 2D Swift Charts。

    章节

    • 0:00 - 简介
    • 1:54 - 绘制 3D 图表
    • 5:05 - 呈现图表
    • 7:03 - 自定

    资源

    • Swift Charts
    • Swift Charts updates
      • 高清视频
      • 标清视频

    相关视频

    WWDC24

    • Swift Charts:矢量图与函数图

    WWDC22

    • 利用图表设计 App 体验
    • 设计实用高效的图表
  • 搜索此视频…

    Hi, I’m Mike, and I'm an engineer on the System Experience team. Today, I'd like to discuss an exciting new feature in Swift Charts. Swift Charts is a framework for creating approachable and stunning charts.

    Charts are used across Apple platforms for things like checking the temperature in Weather, showing battery usage in Settings, and graphing mathematical functions in Math Notes.

    Using the building blocks available in Swift Charts, you can create 2D charts using components such as axis marks, labels, and line plots. But, the plot thickens! New in iOS, macOS, and visionOS 26, Swift Charts now supports 3D charts. That's right, you can now enable people to explore and understand datasets from completely new perspectives. In this session, I'll show how to bring a set of 2D charts to the third dimension by plotting in 3D, I'll cover how surface plots can be used for graphing three-dimensional math functions, and lastly, I'll go over ways you can customize your charts to make them more intuitive and delightful. But first, I have an important announcement: I love penguins! In fact, one of my favorite datasets contains measurements for hundreds of penguins across the Palmer Archipelago in Antarctica. This data includes the beak length, flipper length, and weight of each penguin, and is grouped by the species of penguin: Chinstrap, Gentoo, and Adélie.

    I'll use Swift Charts to fish for insights in this data, and show how plotting in 3D can help visualize the differences between the penguin species. So, I built a 2D chart here to show the relationship between the flipper lengths and weights of the penguins. PointMark is used to plot each penguin's flipper length and weight, and foregroundStyle colors the points by species, and creates the legend in the corner. This is great, and Swift Charts made it easy to construct a chart. This chart shows that Chinstrap and Adélie penguins have similar flipper lengths and weights, while Gentoo penguins have longer flippers and weigh more.

    The penguin dataset also includes beak length, so I also made a chart that plots the beak length and weight. This one shows that Chinstrap and Gentoo penguins have similar beak lengths, while Adélie penguins have shorter beaks.

    Lastly, I made a chart for the beak lengths and flipper lengths, where it seems penguins with longer beaks tend to have longer flippers too.

    Each of these 2D charts are great, and they provide good insights into the relationships between two properties at a time. However, Swift Charts can now take these charts to the next level, by making a single chart that contains all of this data.

    And it's called Chart3D. Chart3D takes familiar concepts from 2D charts, such as scatter plots, and brings them into full 3D. To use a 3D chart, I'll change Chart to Chart3D.

    PointMark works great in Chart3D, and it now takes a Z value. Here, I use the Beak Length.

    I'll set the Z-axis label to "Beak Length" as well. That's it! With a few lines of code and Chart3D, I can immediately see the differences between the penguin species in a fun and interactive way.

    I can use simple gestures to rotate the chart to precise angles and see three clusters of data points.

    I can also view the chart from the sides to compare two properties at a time, similar to if I was viewing the chart in 2D. 3D charts work great when the shape of the data is more important than the exact values. This can happen naturally when the data itself is three-dimensional, especially if it represents a physical position in 3D space. Also, interactivity is key to understanding three-dimensional datasets, so only consider 3D charts if requiring interaction enhances the experience in your app. When it comes to the best representation for your dataset, the choice between 2D and 3D isn't black and white.

    PointMarks, RuleMarks, and RectangleMarks have all been updated for 3D charts. And now, unique to Chart3D, is SurfacePlot Next, I'll take a deep dive into how SurfacePlot works. SurfacePlot is the three-dimensional extension of LinePlot. It plots a mathematical surface containing up to two variables in three dimensions.

    The new SurfacePlot API is similar to the LinePlot API. It accepts a closure that takes two doubles, and returns a double.

    After entering a math expression in the closure, SurfacePlot evaluates the expression for different values of X and Z and creates a continuous surface of the computed Y values. These surfaces can be as intricate, or as simple, as you want.

    To learn more about function plots via the LinePlot API, check out "Swift Charts: Vectorized and function plots" from WWDC24.

    You know what? Now that I'm looking at the penguin dataset again, there appears to be a linear relationship between the beak length and flipper length of a penguin, and how much it weighs. That seems like a reasonable guess, but instead of winging it, I can use SurfacePlot to show a linear regression of the data.

    I've defined a LinearRegression class that estimates a y value based on the independent x and z variables. More specifically, I set the linear regression to estimate the weight of a penguin based on the flipper length and beak length. This linear regression is then used in SurfacePlot to plot the estimated weights as a continuous surface.

    Great! It does seem like there's a linear relationship in this data. The SurfacePlot shows a positive correlation between the flipper length and weight, and there's a slight positive correlation between the beak length and weight as well. Now, I'll go over some of the great ways to customize the style and behavior of Chart3D.

    While I was interacting with the penguin dataset earlier, I noticed that changing the angle of the chart also changes the appearance of the data. This angle is great for showing the clusters of data points. While this angle works well for showing a linear relationship. These angles are known as the pose of the chart.

    It's important to choose an initial pose that provides a good representation for your data.

    For dynamic data where you don't know the values beforehand, try to choose an initial pose that works well for typical datasets of that type.

    The pose of the chart is adjusted using the Chart3DPose modifier, and it takes a Chart3DPose.

    I can set the pose to specific values, such as front, or I can define a custom pose. This initializer takes two parameters: the azimuth, which rotates the chart left and right, and the inclination, which tilts the chart up and down.

    Next, notice how points near the back of the chart are the same size as points near the front of the chart. This can make it easier to compare sizes and distances across the chart regardless of depth.

    It's also great for viewing charts from the side, as it effectively turns a 3D chart into a 2D one.

    This is known as an orthographic camera projection.

    Chart3D offers two camera projections: orthographic, which is the default behavior, and perspective. With Perspective projection, data points farther away appear smaller, and parallel lines converge. This allows for an immersive experience and can help with depth perception.

    The camera projection is set using the chart3DCameraProjection modifier.

    SurfacePlots have a few customization options for the surface styles as well. ForegroundStyle accepts gradients such as LinearGradient or EllipticalGradient, and it supports two new values: heightBased, which colors points on the surface based on the height of the surface at that point, and normalBased, which colors points on the surface based on the angle of the surface at that point.

    There are many other modifiers available for Chart3D, some of which may be familiar from 2D charts. Use these to customize surface styles, PointMark symbols, the chart domain and axis marks, or the behavior of selection. By combining these view modifiers along with PointMark, RuleMark, RectangleMark, and SurfacePlot, there are all sorts of interesting charts that can be achieved. This is just the tip of the iceberg.

    And, 3D charts work and look great on Vision Pro, where it's a natural fit for three dimensional data sets! That's some of the new 3D features coming to Swift Charts. Once you've decided 3D is a good representation for your data, try plotting with Chart3D to bring a new level of depth to your charts, and use Swift Charts' customization APIs to design your own approachable and stunning charts.

    To learn about best practices for incorporating Swift Charts into your apps, check out "Design app experiences with charts" from WWDC22.

    Thank you. I can't wait to see the types of charts that you bring to the third dimension.

    • 2:03 - A scatterplot of a penguin's flipper length and weight

      // A scatterplot of a penguin's flipper length and weight
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        var body: some View {
          Chart(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chartXAxisLabel("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartXScale(domain: 160...240)
          .chartYScale(domain: 2...7)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 2:39 - A scatterplot of a penguin's beak length and weight

      // A scatterplot of a penguin's beak length and weight
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        var body: some View {
          Chart(penguins) { penguin in
            PointMark(
              x: .value("Beak Length", penguin.beakLength),
              y: .value("Weight", penguin.weight)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chartXAxisLabel("Beak Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartXScale(domain: 30...60)
          .chartYScale(domain: 2...7)
          .chartXAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 2:51 - A scatterplot of a penguin's beak length and flipper length

      // A scatterplot of a penguin's beak length and flipper length
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        var body: some View {
          Chart(penguins) { penguin in
            PointMark(
              x: .value("Beak Length", penguin.beakLength),
              y: .value("Flipper Length", penguin.flipperLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chartXAxisLabel("Beak Length (mm)")
          .chartYAxisLabel("Flipper Length (mm)")
          .chartXScale(domain: 30...60)
          .chartYScale(domain: 160...240)
          .chartXAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 3:28 - A scatterplot of a penguin's flipper length, beak length, and weight

      // A scatterplot of a penguin's flipper length, beak length, and weight
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight),
              z: .value("Beak Length", penguin.beakLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 5:19 - A surface plot showing mathematical functions (x * z)

      // A surface plot showing mathematical functions
      
      import SwiftUI
      import Charts
      
      var SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              // (Double, Double) -> Double
              x * z
            }
          }
        }
      }
    • 5:43 - A surface plot showing mathematical functions

      // A surface plot showing mathematical functions
      
      import SwiftUI
      import Charts
      
      var SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              // (Double, Double) -> Double
              (sin(5 * x) + sin(5 * z)) / 2
            }
          }
        }
      }
    • 5:46 - A surface plot showing mathematical functions (-z)

      // A surface plot showing mathematical functions
      
      import SwiftUI
      import Charts
      
      var SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              // (Double, Double) -> Double
              -z
            }
          }
        }
      }
    • 6:19 - Present a linear regression of the penguin data

      // Present a linear regression of the penguin data
      
      import SwiftUI
      import Charts
      import CreateML
      import TabularData
      
      final class LinearRegression: Sendable {
        let regressor: MLLinearRegressor
      
        init<Data: RandomAccessCollection>(
          _ data: Data,
          x xPath: KeyPath<Data.Element, Double>,
          y yPath: KeyPath<Data.Element, Double>,
          z zPath: KeyPath<Data.Element, Double>
        ) {
          let x = Column(name: "X", contents: data.map { $0[keyPath: xPath] })
          let y = Column(name: "Y", contents: data.map { $0[keyPath: yPath] })
          let z = Column(name: "Z", contents: data.map { $0[keyPath: zPath] })
          let data = DataFrame(columns: [x, y, z].map { $0.eraseToAnyColumn() })
          regressor = try! MLLinearRegressor(trainingData: data, targetColumn: "Y")
        }
      
        func callAsFunction(_ x: Double, _ z: Double) -> Double {
          let x = Column(name: "X", contents: [x])
          let z = Column(name: "Z", contents: [z])
          let data = DataFrame(columns: [x, z].map { $0.eraseToAnyColumn() })
          return (try? regressor.predictions(from: data))?.first as? Double ?? .nan
        }
      }
      
      let linearRegression = LinearRegression(
        penguins,
        x: \.flipperLength,
        y: \.weight,
        z: \.beakLength
      )
      
      struct PenguinChart: some View {
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D {
            ForEach(penguins) { penguin in
              PointMark(
                x: .value("Flipper Length", penguin.flipperLength),
                y: .value("Weight", penguin.weight),
                z: .value("Beak Length", penguin.beakLength),
              )
              .foregroundStyle(by: .value("Species", penguin.species))
            }
      
            SurfacePlot(x: "Flipper Length", y: "Weight", z: "Beak Length") { flipperLength, beakLength in
              linearRegression(flipperLength, beakLength)
            }
            .foregroundStyle(.gray)
          }
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 7:50 - Adjust the initial chart pose (Default)

      // Adjust the initial chart pose
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        @State var pose: Chart3DPose = .default
      
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight),
              z: .value("Beak Length", penguin.beakLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chart3DPose($pose)
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 8:02 - Adjust the initial chart pose (Front)

      // Adjust the initial chart pose
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        @State var pose: Chart3DPose = .front
      
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight),
              z: .value("Beak Length", penguin.beakLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chart3DPose($pose)
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 8:09 - Adjust the initial chart pose (Custom)

      // Adjust the initial chart pose
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        @State var pose = Chart3DPose(
          azimuth: .degrees(20),
          inclination: .degrees(7)
        )
      
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight),
              z: .value("Beak Length", penguin.beakLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chart3DPose($pose)
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 9:15 - Adjust the initial chart pose and camera projection

      // Adjust the initial chart pose and camera projection
      
      import SwiftUI
      import Charts
      
      struct PenguinChart: View {
        @State var pose = Chart3DPose(
          azimuth: .degrees(20),
          inclination: .degrees(7)
        )
      
        var body: some View {
          let xLabel = "Flipper Length (mm)"
          let yLabel = "Weight (kg)"
          let zLabel = "Beak Length (mm)"
      
          Chart3D(penguins) { penguin in
            PointMark(
              x: .value("Flipper Length", penguin.flipperLength),
              y: .value("Weight", penguin.weight),
              z: .value("Beak Length", penguin.beakLength)
            )
            .foregroundStyle(by: .value("Species", penguin.species))
          }
          .chart3DPose($pose)
          .chart3DCameraProjection(.perspective)
          .chartXAxisLabel(xLabel)
          .chartYAxisLabel(yLabel)
          .chartZAxisLabel(zLabel)
          .chartXScale(domain: 160...240, range: -0.5...0.5)
          .chartYScale(domain: 2...7, range: -0.5...0.5)
          .chartZScale(domain: 30...60, range: -0.5...0.5)
          .chartXAxis {
            AxisMarks(values: [160, 180, 200, 220, 240]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartYAxis {
            AxisMarks(values: [2, 3, 4, 5, 6, 7]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
          .chartZAxis {
            AxisMarks(values: [30, 40, 50, 60]) {
              AxisTick()
              AxisGridLine()
              AxisValueLabel()
            }
          }
        }
      }
    • 9:24 - Customize the surface styles for a sinc function

      // Customize the surface styles for a sinc function
      
      import SwiftUI
      import Charts
      
      struct SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              let h = hypot(x, z)
              return sin(h) / h
            }
          }
          .chartXScale(domain: -10...10, range: -0.5...0.5)
          .chartZScale(domain: -10...10, range: -0.5...0.5)
          .chartYScale(domain: -0.23...1, range: -0.5...0.5)
        }
      }
    • 9:29 - Customize the surface styles for a sinc function (EllipticalGradient)

      // Customize the surface styles for a sinc function
      
      import SwiftUI
      import Charts
      
      struct SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              let h = hypot(x, z)
              return sin(h) / h
            }
            .foregroundStyle(EllipticalGradient(colors: [.red, .orange, .yellow, .green, .blue, .indigo, .purple]))
          }
          .chartXScale(domain: -10...10, range: -0.5...0.5)
          .chartZScale(domain: -10...10, range: -0.5...0.5)
          .chartYScale(domain: -0.23...1, range: -0.5...0.5)
        }
      }
    • 9:38 - Customize the surface styles for a sinc function (heightBased)

      // Customize the surface styles for a sinc function
      
      import SwiftUI
      import Charts
      
      struct SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              let h = hypot(x, z)
              return sin(h) / h
            }
            .foregroundStyle(.heightBased)
          }
          .chartXScale(domain: -10...10, range: -0.5...0.5)
          .chartZScale(domain: -10...10, range: -0.5...0.5)
          .chartYScale(domain: -0.23...1, range: -0.5...0.5)
        }
      }
    • 9:47 - Customize the surface styles for a sinc function (normalBased)

      // Customize the surface styles for a sinc function
      
      import SwiftUI
      import Charts
      
      struct SurfacePlotChart: View {
        var body: some View {
          Chart3D {
            SurfacePlot(x: "X", y: "Y", z: "Z") { x, z in
              let h = hypot(x, z)
              return sin(h) / h
            }
            .foregroundStyle(.normalBased)
          }
          .chartXScale(domain: -10...10, range: -0.5...0.5)
          .chartZScale(domain: -10...10, range: -0.5...0.5)
          .chartYScale(domain: -0.23...1, range: -0.5...0.5)
        }
      }

Developer Footer

  • 视频
  • WWDC25
  • 将 Swift Charts 扩展成三维图表
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则