I try to make an interactive chart view.
However, in a specific condition, the chart moves iteself unintentionally.
In my test code, this phenomenon occurs when the frame width is over 652.5.
(And, this is also relevant to the height)
Test Movie
OK:
NG:
If you are OK, could you tell me what is a problem in my code?
My test code is the followings:
import SwiftUI
import Charts
struct ContentView: View {
@State var chartW = 652.45
@State var basisDate: Date = Date2StartOnADay(date: Date())
@State var dateRange: Double = 7
@State var dateB = Date2StartOnADay(date: Date()).addingTimeInterval(Double(-7*24*60*60))
@State var dateE = Date2StartOnADay(date: Date())
var body: some View {
VStack {
Slider(value: $chartW, in: 652.45 ... 652.55)
Text("\(chartW)")
DailyChartView(dateB: $dateB, dateE: $dateE, basisDate: $basisDate, dateRange: $dateRange)
.frame(width: chartW, height: 300)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct DailyChartView: View {
@Binding var dateB: Date
@Binding var dateE: Date
@Binding var basisDate: Date
@Binding var dateRange: Double
var yMin: Double = 0.0
var yMax: Double = 100.0
var isFixedYRange: Bool = true
@State var dt: TimeInterval?
var body: some View {
Chart {
AreaMark(x: .value("date", Date().addingTimeInterval(-2*24*60*60)),
y: .value("value", 10)
)
AreaMark(x: .value("date", Date().addingTimeInterval(-24*60*60)),
y: .value("value", 50)
)
AreaMark(x: .value("date", Date()),
y: .value("value", 20)
)
AreaMark(x: .value("date", Date().addingTimeInterval(24*60*60)),
y: .value("value", 70)
)
}
.chartXScale(domain: dateB ... dateE)
.chartYScale(domain: yMin ... yMax)
.chartOverlay { proxy in
GeometryReader { geometry in
Rectangle().fill(.clear).contentShape(Rectangle())
.gesture(DragGesture(minimumDistance: 8)
.onChanged {value in
let startX = value.startLocation.x - geometry[proxy.plotAreaFrame].origin.x
let currentX = value.location.x - geometry[proxy.plotAreaFrame].origin.x
if let startDate: Date = proxy.value(atX: startX),
let currentDate: Date = proxy.value(atX: currentX) {
dt = currentDate.timeIntervalSince(startDate)
dateB = basisDate.addingTimeInterval(-dateRange*24*60*60-dt!)
dateE = basisDate.addingTimeInterval(-dt!)
}
}
.onEnded {_ in
basisDate = dateE
}
)
}
}
.clipped()
}
}
func Date2StartOnADay(date: Date) -> Date {
let cal = Calendar(identifier: .gregorian)
let comp = cal.dateComponents(
[Calendar.Component.year, Calendar.Component.month, Calendar.Component.day,
Calendar.Component.hour, Calendar.Component.minute, Calendar.Component.second, Calendar.Component.nanosecond],
from: date)
let startOnADay = cal.date(from: DateComponents(year: comp.year!, month: comp.month!, day: comp.day))
return startOnADay!
}