2

I'm trying to create a Range chart that looks like a native iOS heart rate chart but with some additional lines on it. chart I need to create. I found the example here https://github.com/jordibruin/Swift-Charts-Examples

I tried to modify my chart by adding a GeometryReader from another chart. FindElement works fine but nothing happens after. So maybe you know a fast answer what should I fix or add to show that line? Or maybe can take a piece of advice on how to get it done using another solution.

private var chart: some View {
            Chart(data, id: \.weekday) { dataPoint in
                Plot {
                    BarMark(
                        x: .value("Day", dataPoint.weekday, unit: .day),
                        yStart: .value("BPM Min", dataPoint.dailyMin),
                        yEnd: .value("BPM Max", dataPoint.dailyMax),
                        width: .fixed(isOverview ? 8 : barWidth)
                    )
                    .clipShape(Capsule())
                    .foregroundStyle(chartColor.gradient)
                }
                .accessibilityLabel(dataPoint.weekday.weekdayString)
                .accessibilityValue("\(dataPoint.dailyMin) to \(dataPoint.dailyMax) BPM")
                .accessibilityHidden(isOverview)
                
            }
            .chartOverlay { proxy in
                GeometryReader { geo in
                    Rectangle().fill(.clear).contentShape(Rectangle())
                        .gesture(
                            SpatialTapGesture()
                                .onEnded { value in
                                    let element = findElement(location: value.location, proxy: proxy, geometry: geo)
                                    if selectedElement?.weekday == element?.weekday {
                                        // If tapping the same element, clear the selection.
                                        selectedElement = nil
                                    } else {
                                        selectedElement = element
                                    }
                                }
                                .exclusively(
                                    before: DragGesture()
                                        .onChanged { value in
                                            selectedElement = findElement(
                                                location: value.location,
                                                proxy: proxy,
                                                geometry: geo
                                            )
                                        }
                                )
                        )
                }
            }
            .chartXAxis {
                AxisMarks(values: .stride(by: ChartStrideBy.day.time)) { _ in
                    AxisTick().foregroundStyle(.white)
                }
            }
            .accessibilityChartDescriptor(self)
            .chartYAxis(.visible)
            .chartXAxis(.visible)
            .frame(height: isOverview ? Constants.previewChartHeight : Constants.detailChartHeight)
    }
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52

1 Answers1

3

Just found that a better solution will be adding another MarkBar with different color and width at the last element to create that vertical line. Also to create that dashed line with annotation it's better to use RuleMark. So the final code will look like that(data is var data = HeartRateData.lastWeek, you can get it from Apple charts examples code):

private var chart: some View {
    Chart(data, id: \.day) { dataPoint in
        Plot {
            BarMark(
                x: .value("Day", dataPoint.day, unit: .day),
                yStart: .value("BPM Min", dataPoint.dailyMin),
                yEnd: .value("BPM Max", dataPoint.dailyMax),
                width: .fixed(6)
            )
            .clipShape(Capsule())
            .foregroundStyle(chartColor.gradient)
        }
        .accessibilityLabel(dataPoint.day.weekdayString)
        .accessibilityValue("\(dataPoint.dailyMin) to \(dataPoint.dailyMax) BPM")
        RuleMark(y: .value("min", 50))
            .foregroundStyle(.gray)
            .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
            .annotation(position: .trailing, alignment: .leading) {
                Text("50")
                    .foregroundStyle(.gray)
                    .offset(x: 10, y: 0)
            }
        RuleMark(y: .value("max", 90))
            .foregroundStyle(.gray)
            .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
            .annotation(position: .trailing, alignment: .leading) {
                Text("90")
                    .foregroundStyle(.gray)
                    .offset(x: 10, y: 0)
            }
        BarMark(
            x: .value("Day", data.last!.day, unit: .day),
            yStart: .value("BPM Min", 40),
            yEnd: .value("BPM Max", 100),
            width: .fixed(2)
        )
        .clipShape(Capsule())
        .foregroundStyle(.white)
    }
    .accessibilityChartDescriptor(self)
    .chartYAxis(.hidden)
    .chartXAxis(.hidden)
    .frame(height: 64)
}