ChartView icon indicating copy to clipboard operation
ChartView copied to clipboard

Can't get legend or x and y labels

Open dadixon opened this issue 3 years ago • 8 comments

v2 ticket

Ticket description:

I'm having trouble getting the legend and labels to show in the ChartGrid with a LineChart. I'm passing in the tuple for the data but only the data is displayed

var demoData: [(String, Double)] = [
      ("M", 8),
      ("T", 2),
      ("W", 4),
      ("T", 6),
      ("F", 12),
      ("S", 9),
      ("S", 2)
]

var body: some View {

   ChartGrid {
       LineChart()
       ChartLabel("")
          .labelStyle(.automatic)
   }
       .data(demoData)
       .chartStyle(ChartStyle(backgroundColor: .white,
                                         foregroundColor: ColorGradient(.blue, .purple)))
}

Screen Shot 2022-06-02 at 10 30 16 AM

dadixon avatar Jun 02 '22 14:06 dadixon

Same

JakobStadlhuber avatar Jun 22 '22 20:06 JakobStadlhuber

The data is already being parsed but it's not implemented in the code for some reason. You can try editing it like below. (this is specifically for BarChart)

Look for the code comment "// BarChartLabel"

import SwiftUI

/// A single row of data, a view in a `BarChart`
public struct BarChartRow: View {
    @EnvironmentObject var chartValue: ChartValue
    @ObservedObject var chartData: ChartData
    @State private var touchLocation: CGFloat = -1.0

    var style: ChartStyle
    
    var maxValue: Double {
        guard let max = chartData.points.max() else {
            return 1
        }
        return max != 0 ? max : 1
    }

	/// The content and behavior of the `BarChartRow`.
	///
	/// Shows each `BarChartCell` in an `HStack`; may be scaled up if it's the one currently being touched.
	/// Not using a drawing group for optimizing animation.
	/// As touched (dragged) the `touchLocation` is updated and the current value is highlighted.
    public var body: some View {
        GeometryReader { geometry in
            HStack(alignment: .bottom,
                   spacing: geometry.frame(in: .local).width / CGFloat(chartData.data.count * 3)) {
                    ForEach(0..<chartData.data.count, id: \.self) { index in
                        BarChartCell(value: chartData.normalisedPoints[index],
                                     index: index,
                                     gradientColor: self.style.foregroundColor.rotate(for: index),
                                     touchLocation: self.touchLocation,
                                     label: self.chartData.values[index]) // BarChartLabel
                            .scaleEffect(self.getScaleSize(touchLocation: self.touchLocation, index: index), anchor: .bottom)
                            .animation(Animation.easeIn(duration: 0.2))
                    }
//                    .drawingGroup()
            }
            .frame(maxHeight: chartData.isInNegativeDomain ? geometry.size.height / 2 : geometry.size.height)
            .gesture(DragGesture()
                .onChanged({ value in
                    let width = geometry.frame(in: .local).width
                    self.touchLocation = value.location.x/width
                    if let currentValue = self.getCurrentValue(width: width) {
                        self.chartValue.currentValue = currentValue
                        self.chartValue.interactionInProgress = true
                    }
                })
                .onEnded({ value in
                    self.chartValue.interactionInProgress = false
                    self.touchLocation = -1
                })
            )
        }
    }

	/// Size to scale the touch indicator
	/// - Parameters:
	///   - touchLocation: fraction of width where touch is happening
	///   - index: index into data array
	/// - Returns: a scale larger than 1.0 if in bounds; 1.0 (unscaled) if not in bounds
    func getScaleSize(touchLocation: CGFloat, index: Int) -> CGSize {
        if touchLocation > CGFloat(index)/CGFloat(chartData.data.count) &&
           touchLocation < CGFloat(index+1)/CGFloat(chartData.data.count) {
            return CGSize(width: 1.4, height: 1.1)
        }
        return CGSize(width: 1, height: 1)
    }

	/// Get data value where touch happened
	/// - Parameter width: width of chart
	/// - Returns: value as `Double` if chart has data
    func getCurrentValue(width: CGFloat) -> Double? {
        guard self.chartData.data.count > 0 else { return nil}
            let index = max(0,min(self.chartData.data.count-1,Int(floor((self.touchLocation*width)/(width/CGFloat(self.chartData.data.count))))))
            return self.chartData.points[index]
        }
}

struct BarChartRow_Previews: PreviewProvider {
     static let chartData = ChartData([6, 2, 5, 6, 7, 8, 9])
    static let chartData2 = ChartData([("M",6), ("T",2), ("W",5), ("T",6), ("F",7), ("S",8), ("S",9)])
    static let chartStyle = ChartStyle(backgroundColor: .white, foregroundColor: .orangeBright)
    static var previews: some View {
      VStack{
        BarChartRow(chartData: chartData, style: chartStyle)
          .border(.red)
          .frame(width: 100, height: 100)
        HStack (alignment: .lastTextBaseline){
          VStack {
              Text("Load")
              Text("400")
          }

          Spacer()
          BarChartRow(chartData: chartData2, style: chartStyle)
            .border(.red)
            .frame(width: 150, height: 50)
          
        }
      }
      .padding([.leading, .trailing],10)

    }
}

BarChartCell

import SwiftUI

/// A single vertical bar in a `BarChart`
public struct BarChartCell: View {
  var value: Double
  var index: Int = 0
  var gradientColor: ColorGradient
  var touchLocation: CGFloat
  var label: String = "" // BarChartLabel
  
  @State private var didCellAppear: Bool = false
  
  public init( value: Double,
               index: Int = 0,
               gradientColor: ColorGradient,
               touchLocation: CGFloat,
               label: String = "") { // BarChartLabel
    self.value = value
    self.index = index
    self.gradientColor = gradientColor
    self.touchLocation = touchLocation
    self.label = label     // BarChartLabel
  }
  
  /// The content and behavior of the `BarChartCell`.
  ///
  /// Animated when first displayed, using the `firstDisplay` variable, with an increasing delay through the data set.
  public var body: some View {
    VStack{ // BarChartLabel - Embed it into a Stack
      BarChartCellShape(value: didCellAppear ? value : 0.0)
        .fill(gradientColor.linearGradient(from: .bottom, to: .top))
        .onAppear {
          self.didCellAppear = true
        }
        .onDisappear {
          self.didCellAppear = false
        }
        .transition(.slide)
        .animation(Animation.spring().delay(self.touchLocation < 0 || !didCellAppear ? Double(self.index) * 0.04 : 0))
      Text(String(label)) // BarChartLabel
        .font(.caption).    // BarChartLabel
    }
  }
}

struct BarChartCell_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            Group {
                BarChartCell(value: 0, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat(), label: "POPO")
                BarChartCell(value: 0.5, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat())
                BarChartCell(value: 0.75, gradientColor: ColorGradient.whiteBlack, touchLocation: CGFloat())
                BarChartCell(value: 1, gradientColor: ColorGradient(.purple), touchLocation: CGFloat())
            }

            Group {
                BarChartCell(value: 1, gradientColor: ColorGradient.greenRed, touchLocation: CGFloat())
                BarChartCell(value: 1, gradientColor: ColorGradient.whiteBlack, touchLocation: CGFloat())
                BarChartCell(value: 1, gradientColor: ColorGradient(.purple), touchLocation: CGFloat())
            }.environment(\.colorScheme, .dark)
        }
    }
}

Screenshot 2022-07-27 at 10 07 59 AM

app4g avatar Jul 27 '22 02:07 app4g

struct ContentView: View {
//  var demoData: [String, Double] = [("M",1.0),("T",2.0),("W",4.0), ("T", 8.0), ("F",10.0), ("S",9.0),("S", 2.0)]
  var demoData: [(String, Double)] = ([("M",9), ("T",2), ("W",5), ("T",6), ("F",7), ("S",8), ("S",9)])
  var demoData2: [(String, Double)] = ([("M",1), ("T",11), ("W",12), ("T",6), ("F",11), ("S",0), ("S",7)])
  var demoData3: [(String, Double)] = ([("M",1.55), ("T",2.1), ("W",1.6), ("T",0.3), ("F",0.9), ("S",1.3), ("S",2.0)])

    var body: some View {
    VStack {
      Text("Hello, world!")
      
      HStack{
        CardView(showShadow: true) {
          Text("Load")
            .padding([.leading, .top],10)
            .font(.caption)
          ChartLabel("400", type: .custom(size: 10, padding: EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0), color: Color(UIColor.secondaryLabel)), format: "%0.0f")
          BarChart()
            .padding(.horizontal,10)
            .padding(.bottom,5)
        }
        .data(demoData)
        .chartStyle(ChartStyle(backgroundColor: .white,
                               foregroundColor: [ColorGradient(.blue, .purple)]
                              ))

        CardView(showShadow: true) {
          Text("Distance")
            .padding([.leading, .top],10)
            .font(.caption)
          ChartLabel("400km", type: .custom(size: 10, padding: EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0), color: Color(UIColor.secondaryLabel)), format: "%0.0f")
          BarChart()
            .padding(.horizontal,10)
            .padding(.bottom,5)
        }
        .data(demoData2)
        .chartStyle(ChartStyle(backgroundColor: .white,
                               foregroundColor: [ColorGradient(.blue, .purple)]
                              ))

        CardView(showShadow: true) {
          Text("Time")
            .padding([.leading, .top],10)
            .font(.caption)
          ChartLabel("10h 50m", type: .custom(size: 10, padding: EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0), color: Color(UIColor.secondaryLabel)), format: "%0.1f")
          BarChart()
            .padding(.horizontal,10)
            .padding(.bottom,5)
        }
        .data(demoData3)
        .chartStyle(ChartStyle(backgroundColor: .white,
                               foregroundColor: [ColorGradient(.blue, .purple)]
                              ))

      }
      .frame(width: .infinity, height: 150, alignment: .center)

    }
  }
}
Screenshot 2022-07-27 at 11 32 44 AM

app4g avatar Jul 27 '22 03:07 app4g

@app4g that looks nice, you forked already ChartView but without changes, do you have plans to implement the above shown changes? Would be nice :)

JakobStadlhuber avatar Jul 27 '22 11:07 JakobStadlhuber

@JakobStadlhuber I just made the changes locally and am just still playing around / testing it. eg: it doesn't honour dark mode in cardview

and trying to get this to work. In the interim (screenshot) shows just changing it from .white to .lightgray jus to see if it works.

Screenshot 2022-07-27 at 8 00 00 PM

Yeah.. I've forked It but have yet to sync it. Possibly over the next few days as I play more w/ it.

app4g avatar Jul 27 '22 12:07 app4g

Sounds good @app4g I will follow your repo. Do you think it is possible to get a y axis too?

JakobStadlhuber avatar Jul 27 '22 13:07 JakobStadlhuber

@app4g some news with the forked repo?

JakobStadlhuber avatar Aug 02 '22 19:08 JakobStadlhuber

Forked Repo : https://github.com/app4g/ChartView

Here is the Demo Project or rather the code I was working on to see /make the changes https://github.com/app4g/ChartViewV2-Demo-Project

app4g avatar Aug 03 '22 06:08 app4g