I am making a chart using Swift Charts. I have added pinch zoom and panning to the chart. The problem I have can be seen in this video I made. The annotations on the BarMark
s collide with the edges of the chart's parent view, creating this "squeeze" effect. How can I disable the "collisions"? Here's the source code of the chart:
VStack {
GeometryReader { proxy in
Chart {
ForEach(viewModel.motionData) { data in
BarMark(x: .value("", data.x), y: .value("", viewModel.scaleMotionValue(data.y)), width: .fixed(barWidth))
.clipShape(RoundedRectangle(cornerRadius: 4))
.foregroundStyle(viewModel.getMotionBarAppearance(.init(rawValue: data.y)!))
.annotation(position: .top, alignment: .center, spacing: 0) {
Text(String(data.x))
}
}
ForEach(viewModel.dbData) { data in
LineMark(x: .value("", data.x), y: .value("", data.y))
.foregroundStyle(HiddenCustomerName.brownish)
}
.interpolationMethod(.catmullRom)
BarMark(xStart: .value("", viewModel.feelscapeData[0]), xEnd: .value("", viewModel.feelscapeData[1]), y: .value("", -15), height: 20)
.clipShape(RoundedRectangle(cornerRadius: 4))
.foregroundStyle(Appearance.theme.iconColor)
.annotation(position: .overlay, alignment: .center, spacing: 2) {
Text("hello")
}
}
.chartYScale(domain: [yStart, yEnd])
.chartXScale(range: viewModel.domain ?? 0.0...1.0)
.chartXAxis {
AxisMarks(position: .bottom, values: Array(stride(from: 0, to: viewModel.motionData.count, by: 1))) { axis in
AxisTick(length: viewModel.getAxisTickLength(axis), stroke: StrokeStyle(lineWidth: 2, lineCap: .round))
.foregroundStyle(.black)
.offset(y: 10)
}
}
.chartYAxis {
AxisMarks(preset: .automatic) { axis in
AxisTick()
AxisGridLine()
}
}
.gesture(SimultaneousGesture(magnificationGesture, dragGesture))
.onAppear {
if viewModel.domain == nil {
let lowerBound = (proxy.size.width - (CGFloat(viewModel.motionData.count) * (barWidth + 2))) - barWidth
let upperBound = proxy.size.width - barWidth
viewModel.domain = lowerBound...upperBound
}
}
.onChange(of: viewModel.motionData.count) { newValue in
viewModel.calculateDimensions(plotEnd: proxy.size.width, barWidth: barWidth)
}
}
}
.frame(height: 140)
.padding([.top, .bottom])
I have tried adding .edgesIgnoringSafeArea(.all)
and .ignoresSafeArea(.all)
to the Text
inside the .annotation(..)
. The problem goes away if I just remove the annotations, but I need the annotation on the horizontal BarMark
. The rest is there just to demo the problem.
Okay so the solution was to add the domain
parameter to the .chartXScale(..)
modifier.
Before:
.chartXScale(range: viewModel.domain ?? 0.0...1.0)
After:
.chartXScale(domain: [0, viewModel.motionData.count - 1], range: viewModel.domain ?? 0.0...1.0)
Why does this affect how the mark annotations interact with the view bounds is beyond me.