Swift Charts: .chartYScale only seems to work with increments of 100?
Asked Answered
D

3

4

Given this is a heart rate chart, I'm trying to make the chart's Y max scale 210 (bpm), e.g. .chartYScale(domain: 0 ... 210) however it only seems to scale correctly if I pass in 200 or 300, anything in between doesn't work. Is this intended or a bug?

import SwiftUI
import Charts
import HealthKit

struct TestYAxisRangeChart: View {
    
    let heartRateData = [80.0, 90.0, 120.0, 150.0, 160.0, 140.0, 125.0, 110.0, 88.0]
    
    var body: some View {
        Chart {
            ForEach(heartRateData, id: \.self) { sample in
                LineMark(
                    x: .value("", heartRateData.firstIndex(of: sample)!),
                    y: .value("HR", sample))
                .foregroundStyle(Color.red)
                
            }

        }
        .chartYAxis{
            AxisMarks(position: .leading)
        }
        .frame(height: 300)
        .padding(.horizontal)
        .chartYScale(domain: 0 ... 210)
    }
    
}

struct TestYAxisRangeChart_Previews: PreviewProvider {
    static var previews: some View {
        TestYAxisRangeChart()
    }
}
Dynast answered 7/7, 2022 at 17:58 Comment(3)
It depends on ScaleType, see documentation for that parameter, in your case automatic is .linear.Wileen
@Wileen thanks I did but not sure what ScaleType would give me the granularity I am looking for?Dynast
Were you able to come up with a better solution?Volley
W
8

Maybe you just need this (Xcode 14b4 / iOS 16):

let yValues = stride(from: 0, to: 220, by: 10).map { $0 } // << here !!

var body: some View {
    Chart {
        ForEach(heartRateData, id: \.self) { sample in
            LineMark(
                x: .value("", heartRateData.firstIndex(of: sample)!),
                y: .value("HR", sample))
            .foregroundStyle(Color.red)
            
        }

    }
    .chartYAxis{
        AxisMarks(position: .leading, values: yValues)  // << here !!
    }

demo

Test module on GitHub

Wileen answered 7/7, 2022 at 18:35 Comment(2)
Thanks this works but is a little heavy handed, I'd rather the library decide now many Y Axis labels to create but just give it a min and max. Actually I'd just like the Y Axis to be the data.min and data.max.Dynast
This works but a way is needed to calculate the max Y value in the stride call (220 in this case). I have been trying to figure out a way to calculate the value. I have tried to calculate that value to insert in stride call to no avail. I had hoped to use a property initializer but that errors out with "Cannot use instance member 'yVal' within property initializer; property initializers run before 'self' is available" error. Any ideas on how to do this? ThanksAcclimate
A
5

chartYScale can be passed an automatic domain excluding 0 if you just want to have the scale not start with 0 (e.g. .chartYScale(domain: .automatic(includesZero: false))), but depending on your data and the computed y stride, 0 may still be visible. To include 210 without setting an explicit domain or values, you could always just plot an invisible point mark at 210, but this will lead to a rounded max y axis value (e.g 250-300 depending on your data). The only way to make the max exactly 210 is taking full control of the axis via values or domain as suggested by others.

Algebraic answered 4/12, 2022 at 7:41 Comment(0)
B
-2

Use range instead of domain, and plotDimension uses the data range automatically, IIRC:

.chartYScale(range: .plotDimension(padding: 20))
Bullace answered 7/11, 2022 at 15:14 Comment(1)
I tested this but it still shows a range of 0 - 200 even though the data is well inside of that, 60-180.Dynast

© 2022 - 2025 — McMap. All rights reserved.