I am making a Mac app that displays data using a Swift Charts line chart. Once the data is displayed I want to be able to "zoom" the X and Y axis using a drag gesture. For example if fully displaying all of the data required a X axis that went from 0 to 100 I want the X axis to zoom as I drag the mouse from left to right. So for example maybe when I am done dragging the X scale might display from 25 to 50 and only display the data in that range.
I have been trying to use.
.chartXScale(domain:) .chartYScale(domain:)
These modifiers do change the scale however it causes the line to go off of the chart.
Here is the code I am using. There is some debug code here and I have not implemented the zoom on the Y axis yet but it should be a working example for the X axis that corresponds to the examples I have shown.
struct MainView: View {`
@ObservedObject var globalData = GlobalData.shared
@State var multiSelection = Set<UUID>()
@State var currentXAxisScale = 0
@State var xAxisChangeAmount = 0
@State var xMin = 0.0
@State var xMax = 20.0
@State var yMin = 0.0
@State var yMax = 15.0
@State var startingXPostion = 0.0
@State var startingYPositon = 0.0
@State var currentXPositon = 0.0
@State var currentYPosition = 0.0
@State var xMinString = ""
@State var xMaxString = ""
@State var yMinString = ""
@State var yMaxString = ""
@State var isDataLoaded = false
@State var firstPoint = true
let amountFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.zeroSymbol = ""
return formatter
}()
var body: some View {
HStack {
VStack {
HStack {
Button("Open File") {
let readData: ReadInFile = ReadInFile()
let fileName = readData.openFileDialog()
}
.padding()
GroupBox(label: Text("Axis Scale")) {
HStack {
Button("Update") {
xMin = Double(xMinString)!
xMax = Double(xMaxString)!
yMin = Double(yMinString)!
yMax = Double(yMaxString)!
}
VStack {
TextField("X Min", text: $xMinString)
TextField("X Max", text: $xMaxString)
TextField("Y Min", text: $yMinString)
TextField("Y Max", text: $yMaxString)
}
}
}
}
.padding()
List(selection: $multiSelection) {
ForEach(globalData.dataSeriesGroup , id: \.id) { file in
Section(header: Text(file.fileName)){
ForEach(file.dataSeries, id: \.id){ series in
HStack{
Text(series.Name)
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
print("touched item \(series.Name) \(file.fileName)")
isDataLoaded = false
globalData.displayDataChannle(fileName: file.fileName, channleName: series.Name)
setFullScale()
isDataLoaded = true
}
}
}
}
}
.padding([.leading, .bottom])
}
.frame(width: 300.0)
//Just a test to see a chart
Chart(globalData.displayChannle) {
LineMark(
x: .value("Time", $0.time),
y: .value("Value", $0.value)
)
}
.chartXScale(domain: xMin...xMax)
.chartYScale(domain: yMin...yMax)
.chartOverlay{ displayChart in
GeometryReader { geometry in
Rectangle().fill(.clear).contentShape(Rectangle())
.gesture(
DragGesture()
.onChanged { value in
let origin = geometry[displayChart.plotAreaFrame].origin
let location = CGPoint(
x: value.location.x - origin.x,
y: value.location.y - origin.y
)
if(firstPoint == false){
let (currentXposition1, currentYpostion1) = displayChart.value(at: location, as: (Double, Double).self)!
currentXPositon = currentXposition1
currentYPosition = currentYpostion1
print("Current Location: \(currentXposition1), \(currentYpostion1)")
zoom()
}
if(firstPoint == true){
firstPoint = false
let(startXPosition1, startYPosition1) = displayChart.value(at: location, as: (Double, Double).self)!
startingXPostion = startXPosition1
startingYPositon = startYPosition1
print("Starting Location: \(startXPosition1), \(startYPosition1)")
}
}
.onEnded { value in
firstPoint = true
print("STOPPED")
}
)
}
}
.padding()
}
}
func setFullScale()
{
let count = globalData.displayChannle.count
yMin = 0
yMax = 0
xMax = 0
xMin = 0
xMax = globalData.displayChannle[count - 1].time
for point in globalData.displayChannle
{
if(point.value > yMax)
{
yMax = point.value
}
if(point.value < yMin)
{
yMin = point.value
}
}
}
func zoom()
{
if(currentXPositon > startingXPostion)
{
if(abs(xMax - xMin) > 4)
{
xMin = xMin + 2
xMax = xMax - 2
}
}
if(currentXPositon < startingXPostion)
{
if(abs(xMax - xMin) > 4)
{
xMin = xMin - 2
xMax = xMax + 2
}
}
}
}
Use the .clipped()
view modifier on your Chart