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)
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!
}