iosswiftswiftuiswiftcharts

Is it possible to create a View component that only returns ChartContent?


I have a complex chart that can render variety of graphs in different styles and am working on abstracting these variants into separate view component, one of such examples is

import SwiftUI
import Charts


struct ExerciseChartPlot: View {
  
  // Public Variables
  let isLineChart: Bool
  let xLabel: String
  let yLabel: String
  let calendarUnit: Calendar.Component
  let date: Date
  let value: Double
  
  // Body
  var body: some View {
    if isLineChart {
      LineMark(x: .value(xLabel, date, unit: calendarUnit), y: .value(yLabel, value))
        .foregroundStyle(Color._green)
        .symbol(.circle)
      AreaMark(x: .value(xLabel, date, unit: calendarUnit), y: .value(yLabel, value))
        .foregroundStyle(LinearGradient(colors: [._green.opacity(0.2), .clear], startPoint: .top, endPoint: .bottom))
    } else {
      BarMark(x: .value(xLabel, date, unit: calendarUnit), y: .value(yLabel, value))
        .foregroundStyle(Color._green.gradient)
    }
  }
}

But it results in error of "Static method 'buildExpression' requires that 'some ChartContent' conform to 'View'"

I assume this is due to me returning components without wrapping them in Chart. I'd like to keep Chart component on a "higher level" in ui tree and only have these smaller components return chart elements that need to be rendered within it.

Is it possible to achieve this somehow?


Solution

  • What you are trying to create is not a View, but a ChartContent. Conform your struct to ChartContent instead:

    struct ExerciseChartPlot: ChartContent {
      
      // Public Variables
      ...
      
      // Body
      var body: some ChartContent {
          ...
      }
    }
    

    You can then use this just like any other ChartContent in Chart { ... }.

    struct SomeView: View {
        var body: some View {
            Chart {
                ExerciseChartPlot(...)
            }
        }
    }