View in English

  • 메뉴 열기 메뉴 닫기
  • Apple Developer
검색
검색 닫기
  • Apple Developer
  • 뉴스
  • 둘러보기
  • 디자인
  • 개발
  • 배포
  • 지원
  • 계정
페이지에서만 검색

빠른 링크

5 빠른 링크

비디오

메뉴 열기 메뉴 닫기
  • 컬렉션
  • 주제
  • 전체 비디오
  • 소개

WWDC25 컬렉션으로 돌아가기

  • 소개
  • 요약
  • 자막 전문
  • 코드
  • Swift Charts를 3차원으로 가져오기

    Chart3D를 사용하여 2D Swift Charts를 3차원으로 가져오고 완전히 새로운 관점에서 데이터 세트를 시각화하는 방법을 알아보세요. 3D에 데이터를 플롯하고, 수학적 표면을 시각화하며, 카메라부터 재료에 이르기까지 모든 것을 사용자 정의하여 3D Charts를 보다 직관적이고 재미있게 만들어보세요. 이 세션을 최대한 활용하려면 2D Swift Charts 만드는 방법을 먼저 익히는 것이 좋습니다.

    챕터

    • 0:00 - 서론
    • 1:54 - 3D에서 플롯하기
    • 5:05 - 표면 플롯
    • 7:03 - 사용자 정의

    리소스

    • Swift Charts
    • Swift Charts updates
      • HD 비디오
      • SD 비디오

    관련 비디오

    WWDC24

    • Swift Charts: 벡터화된 플롯과 함수 플롯

    WWDC22

    • 차트로 앱 경험 디자인
    • 효과적인 차트 디자인
  • 비디오 검색…

    안녕하세요, System Experience 팀의 엔지니어 Mike입니다 오늘은 Swift Charts의 흥미로운 새 기능을 소개하겠습니다 Swift Charts는 접근성이 뛰어나고 멋진 차트를 만드는 프레임워크죠

    차트는 Apple 플랫폼에서 사용되며 용도는 날씨에서 온도 확인

    설정에서 배터리 사용량 표시 수학 노트에서 수학 함수 그래프화 등입니다

    Swift Charts에서 사용 가능한 빌딩 블록을 사용해 축 표시, 레이블, 선 플롯 같은 2D 차트를 만들 수 있는데요

    이제 iOS, macOS, visionOS 26에서 Swift Charts가 3D 차트를 지원해 플롯이 다각화됩니다 완전히 다른 시각에서 데이터세트를 탐색하고 이해할 수 있는 거죠

    이 세션에서는 3D 플롯으로 2D 차트 세트를 3차원으로 가져오는 방법을 설명합니다 표면 플롯을 사용해 3차원 수학 함수를 그래프화하는 방법을 다루고요 마지막으로 더 직관적이고 멋지도록 차트를 맞춤화하는 방법을 살펴봅니다 먼저 한 가지 말씀드릴 것이 있습니다

    저는 펭귄을 좋아합니다

    제가 가장 좋아하는 데이터세트는 남극 팔머 제도의 펭귄 수백 마리에 대한 측정값을 담고 있답니다

    이 데이터는 각 펭귄의 부리 길이, 지느러미 길이 및 몸무게를 포함하며

    펭귄종에 따라 턱끈, 젠투, 아델리로

    그룹화됩니다

    Swift Charts를 사용해 이 데이터에서 인사이트를 얻고 3D에서 플롯해 펭귄종 간 차이점을 시각화하는 방법을 보여 드리죠

    이렇게 펭귄의 지느러미 길이와 몸무게 간의 관계를 나타낸 2D 차트를 빌드했습니다 PointMark로 각 펭귄의 지느러미 길이와 몸무게를 플롯하고 foregroundStyle로 종별 점을 색칠하고 모서리에 범례를 만듭니다

    이렇게 Swift Charts로 차트를 쉽게 만들었습니다 이 차트에 따르면 턱끈펭귄과 아델리펭귄은 지느러미 길이 및 몸무게가 비슷하지만 젠투펭귄은 지느러미가 더 길고 몸무게도 더 무겁습니다

    펭귄 데이터세트에는 부리 길이도 있으므로 부리 길이와 무게를 플롯한 차트도 만들었습니다

    턱끈펭귄과 젠투펭귄은 부리 길이가 비슷하고 아델리펭귄은 부리가 더 짧군요

    마지막으로 부리 길이와 지느러미 길이 차트를 만들었습니다 부리가 긴 펭귄은 지느러미도 긴 경향을 보입니다

    각 2D 차트 모두 훌륭하며 한 번에 두 속성 간 관계에 대한 유용한 인사이트를 제시합니다

    하지만 Swift Charts로 차트의 수준을 한 단계 높일 수 있습니다 데이터 전체를 포함하는 단일 차트를 만드는 거죠

    바로 Chart3D입니다 Chart3D는 산포도 등 2D 차트의 익숙한 콘셉트를 3D로 온전히 구현합니다

    3D 차트 사용을 위해 차트를 Chart3D로 변경하겠습니다

    Pointmark는 Chart3D에서 잘 작동하고 Z 값을 취합니다 여기서는 부리 길이를 사용하죠

    Z축 레이블을 ‘부리 길이‘로 설정하면 끝이죠

    코드와 Chart3D 몇 줄로 펭귄종의 차이점을 재미있는 방식으로 바로 알 수 있죠

    간단한 제스처로 차트를 정확한 각도로 회전하고 데이터 포인트 클러스터 3개를 확인할 수 있습니다

    옆에서 차트를 보면 2D에서 보는 것처럼 한 번에 두 속성을 비교할 수도 있죠

    3D 차트는 데이터 도형이 정확한 값보다 더 중요할 때 잘 작동합니다 특히 3D 공간에서 물리적 위치를 나타낼 때 데이터 자체가 3차원이면 자연스레 발생할 수 있죠

    또한 3차원 데이터세트를 이해할 때 상호작용이 중요하므로 상호작용이 앱 경험을 향상하는 경우에만 3D 차트를 고려하세요 데이터세트의 최적 표현과 관련해 2D와 3D 중 선택하는 것은 흑백의 문제가 아닙니다

    PointMark, RuleMark, RectangleMark 모두 3D 차트에 맞게 업데이트되었습니다 그리고 Chart3D의 고유 항목으로 SurfacePlot이 있습니다

    다음으로 SurfacePlot의 작동 방식을 살펴보겠습니다

    SurfacePlot은 LinePlot의 3차원 확장 프로그램입니다 3차원에서 변수가 최대 두 개인 수학적 표면을 플롯합니다

    새로운 SurfacePlot API는 LinePlot API와 비슷합니다

    Double 두 개를 취하고 Double을 반환하는 클로저를 허용합니다

    클로저에 수학 표현식을 입력하면 SurfacePlot이 X와 Z의 다른 값에 대해 표현식을 평가하고 계산된 Y 값의 연속적 표면을 생성합니다 이러한 표면은 복잡하거나 간단하게 만들 수 있습니다

    LinePlot API를 통한 함수 플롯에 대한 자세한 내용은 WWDC24 “Swift Charts: 벡터화와 함수 플롯”을 확인하세요

    지금 펭귄 데이터세트를 다시 보니 펭귄의 부리 길이 및 지느러미 길이와 몸무게 간에 선형 관계가 있는 것 같습니다 합리적인 추측으로 보이지만 지레짐작 대신 SurfacePlot을 사용해 데이터의 선형 회귀를 나타낼 수 있습니다

    독립적인 x 및 z 변수를 기반으로 y 값을 추정하는 LinearRegression 클래스를 정의했습니다 구체적으로 말하면 펭귄의 지느러미 길이와 부리 길이로 몸무게를 추정하는 선형 회귀를 설정했습니다

    SurfacePlot에서 이 선형 회귀를 사용해 추정된 가중치를 연속적 표면으로 플롯합니다

    예상대로 이 데이터에 선형 관계가 있군요 SurfacePlot은 지느러미 길이와 몸무게 사이 간에 양의 상관관계 그리고 부리 길이와 몸무게 간에 미세한 양의 상관관계를 보여 줍니다

    이제 Chart3D의 스타일 및 동작을 맞춤화하는 데 좋은 방법을 확인하겠습니다

    앞서 펭귄 데이터세트와 상호작용할 때 차트의 각도 변화와 데이터의 모양 변화가 눈에 띄었는데요

    이 각도는 데이터 포인트의 클러스터를 나타내기 좋습니다 이 각도는 선형 관계를 나타내기에도 적합하고요

    이 각도를 차트의 포즈라고 합니다

    데이터를 잘 표현하는 초기 포즈를 선택하는 것이 중요합니다

    사전에 값을 알 수 없는 동적 데이터라면 해당 유형의 일반 데이터세트에 맞는 초기 포즈를 선택하세요

    차트의 포즈는 Chart3DPose 수정자를 사용해 조정되며 Chart3DPose가 필요합니다

    포즈를 front 등 특정 값으로 설정하거나

    맞춤형 포즈를 정의할 수 있습니다 이 이니셜라이저는 매개변수 두 개를 사용합니다 차트를 좌우로 회전하는 azimuth와

    차트를 위아래로 기울이는 inclination입니다

    다음으로 차트 뒤쪽 근처의 점을 보면 차트 앞쪽 근처의 점과 크기가 같죠

    이러면 차트 전체에서 깊이와 관계없이 크기와 거리를 비교하기 더 쉽습니다

    측면에서 봐도 좋습니다 3D 차트를 2D로 효과적으로 전환해 주거든요

    이것을 정사 카메라 투영이라고 하는데요

    Chart3D의 카메라 투영은 두 가지입니다 정사 투영은 기본 동작과 관점에 해당합니다 원근 투영에서는 데이터 포인트가 멀수록 작게 보이고 평행선은 수렴합니다 따라서 몰입형 경험이 가능하며 깊이 인지에 유용할 수 있습니다

    chart3DCameraProjection 수정자로 카메라 투영을 설정합니다

    SurfacePlot에는 표면 스타일 맞춤화 옵션도 있습니다

    ForegroundStyle은 LinearGradient EllipticalGradient 같은 그라디언트를 허용하며 두 가지 새로운 값을 지원합니다 지점의 표면 높이를 기준으로 표면의 점에 색상을 적용하는 heightBased와

    지점의 표면 각도를 기준으로 표면의 색상을 적용하는 normalBased입니다

    Chart3D에 사용 가능한 다른 수정자도 많은데 일부는 2D 차트에서 익숙한 것일 수 있습니다 표면 스타일, PointMark 기호 차트 도메인 및 축 표시 또는 선택 내용의 동작을 맞춤화하는 데 사용합니다

    이러한 보기 수정자와 PointMark RuleMark, RectangleMark SurfacePlot을 결합하면 각종 흥미로운 차트를 만들 수 있습니다 이것은 빙산의 일각에 불과합니다

    또 3D 차트는 Vision Pro에서 잘 작동하고 멋지게 보입니다 3차원 데이터 세트와 자연스럽게 맞으니까요

    이렇게 Swift Charts에 추가되는 새로운 3D 기능을 알아보았습니다

    3D가 데이터를 표현하는 데 알맞다고 판단된다면 Chart3D로 플롯해 차트에 새롭게 깊이를 더해 보세요 Swift Charts의 맞춤화 API를 사용해 접근하기 쉽고 멋진 차트를 직접 디자인해 보세요

    앱에 Swift Charts를 통합하는 모범 사례는 WWDC22의 “차트로 앱 경험 디자인”을 확인하세요

    감사합니다, 여러분이 만드실 3차원 차트가 정말 기대되네요

    • 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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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: Data,
          x xPath: KeyPath,
          y yPath: KeyPath,
          z zPath: KeyPath
        ) {
          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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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 {
          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("Flipper Length (mm)")
          .chartYAxisLabel("Weight (kg)")
          .chartZAxisLabel("Beak Length (mm)")
          .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)
        }
      }
    • 0:00 - 서론
    • Swift Charts는 iOS, macOS, visionOS 26에서 3D 차트를 지원합니다. 사람들이 새로운 관점에서 데이터를 탐색할 수 있도록 하는 3D 시각화를 만듭니다.

    • 1:54 - 3D에서 플롯하기
    • 3D 차트를 만드는 것은 2D 차트를 만드는 방법과 비슷합니다. 단일 3D 차트가 2D 차트의 다양한 관점을 어떻게 표현할 수 있는지 살펴보세요. PointMark 및 Chart3D를 사용하여 3D 산포도를 작성합니다. 3D 차트는 상호작용이 가능하고 회전시켜 데이터 클러스터와 같은 다양한 통찰을 얻을 수 있습니다. 3D를 위해 2D 차트의 여러 마크가 재구성되었습니다.

    • 5:05 - 표면 플롯
    • SurfacePlot은 3D에 있는 2개 변수의 함수를 플로팅하여 연속적인 표면을 생성합니다. x와 z에 대한 y 값을 생성하는 표현식을 제공하세요. 이러한 표면은 평면처럼 단순할 수도 있고 아니면 흥미로운 모양으로 더 복잡할 수도 있습니다.

    • 7:03 - 사용자 정의
    • Swift Charts에는 3D 차트의 모양을 사용자 정의할 수 있는 수정자가 있습니다. chart3DPose 수정자를 사용하여 차트의 시야각을 설정합니다. chart3DCameraProjection 수정자를 사용하여 차트의 3D 동작을 지정합니다. 표면 플롯용 새로운 전경 스타일도 있습니다. 특정 지점의 표면 각도에 붙여진 지점이나 상대적 높이의 색상을 변경합니다. 이러한 기술과 2D 차트의 많은 수정자는 3D 차트를 아름답게 사용자 정의합니다.

Developer Footer

  • 비디오
  • WWDC25
  • Swift Charts를 3차원으로 가져오기
  • 메뉴 열기 메뉴 닫기
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    메뉴 열기 메뉴 닫기
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    메뉴 열기 메뉴 닫기
    • 손쉬운 사용
    • 액세서리
    • 앱 확장 프로그램
    • App Store
    • 오디오 및 비디오(영문)
    • 증강 현실
    • 디자인
    • 배포
    • 교육
    • 서체(영문)
    • 게임
    • 건강 및 피트니스
    • 앱 내 구입
    • 현지화
    • 지도 및 위치
    • 머신 러닝 및 AI
    • 오픈 소스(영문)
    • 보안
    • Safari 및 웹(영문)
    메뉴 열기 메뉴 닫기
    • 문서(영문)
    • 튜토리얼
    • 다운로드(영문)
    • 포럼(영문)
    • 비디오
    메뉴 열기 메뉴 닫기
    • 지원 문서
    • 문의하기
    • 버그 보고
    • 시스템 상태(영문)
    메뉴 열기 메뉴 닫기
    • Apple Developer
    • App Store Connect
    • 인증서, 식별자 및 프로파일(영문)
    • 피드백 지원
    메뉴 열기 메뉴 닫기
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(영문)
    • News Partner Program(영문)
    • Video Partner Program(영문)
    • Security Bounty Program(영문)
    • Security Research Device Program(영문)
    메뉴 열기 메뉴 닫기
    • Apple과의 만남
    • Apple Developer Center
    • App Store 어워드(영문)
    • Apple 디자인 어워드
    • Apple Developer Academy(영문)
    • WWDC
    Apple Developer 앱 받기
    Copyright © 2025 Apple Inc. 모든 권리 보유.
    약관 개인정보 처리방침 계약 및 지침