添加自定义DragGesture时如何保留SwiftUI Chart原生横向滚动
解决SwiftUI Chart自定义拖拽后横向滚动失效的问题
你的问题出在自定义的DragGesture拦截了Chart原生的滚动手势——哪怕用了simultaneousGesture也没用,因为你设置了minimumDistance: 0,这个手势会捕获所有触摸事件,直接覆盖了Chart的滚动逻辑。
其实根本不需要自己写拖拽手势,利用Chart自带的chartXSelection就能实现「拖拽结束后选中状态不重置」的需求,还能完整保留原生滚动功能,具体修改步骤如下:
- 在
SleepScoreChart里新增一个本地状态变量,用来接收Chart原生的选中值:
@State private var tempSelectedDate: Date?
- 把原来绑定
rawSelectedDate的chartXSelection改成绑定这个本地变量,再通过onChange监听本地变量的变化,只在它有值时才更新rawSelectedDate:
.chartXSelection(value: $tempSelectedDate) .onChange(of: tempSelectedDate) { newDate in // 仅在选中有效日期时更新绑定值,拖拽结束后tempSelectedDate会变为nil,不会影响rawSelectedDate if let newDate { rawSelectedDate = newDate } }
- 删除掉原来的
.chartOverlay和里面的自定义拖拽手势逻辑。
修改后的完整代码:
struct ScoreChartModel: Identifiable { let id = UUID() var score: Int var date: Date } struct SleepScoreChart: View { @State var chartDisplayData: [ScoreChartModel] @Binding var rawSelectedDate: Date? @State private var tempSelectedDate: Date? // 新增本地状态 var body: some View { Chart { ForEach(chartDisplayData) { chartData in PointMark( x: .value("sleep date", chartData.date, unit: .day), y: .value("sleep score", chartData.score) ) .interpolationMethod(.catmullRom) } } .chartXAxis { AxisMarks(values: .stride(by: .day)) { _ in AxisTick() AxisGridLine() AxisValueLabel(format: .dateTime.weekday(.abbreviated)) } } .chartYScale(domain: 0...120) .chartYAxis(.hidden) .chartScrollableAxes(.horizontal) .chartXVisibleDomain(length: 3600 * 24 * 7) .chartXSelection(value: $tempSelectedDate) // 绑定本地变量 .onChange(of: tempSelectedDate) { newDate in // 监听变化更新绑定值 if let newDate { rawSelectedDate = newDate } } .chartScrollPosition(initialX: Date()) } }
这样修改后,既保留了Chart原生的横向滚动功能,又能实现拖拽选择数据点后rawSelectedDate不会被重置的需求——原生的chartXSelection在拖拽结束后会把tempSelectedDate设为nil,但我们只在它有值的时候更新rawSelectedDate,所以绑定值会一直保留最后选中的日期。
内容的提问来源于stack exchange,提问作者Michał Tomys
相关产品推荐
相关产品推荐

