Create by code:
Chart {
ForEach(viewModel.historyChar) { chartItem in
AreaMark(
x: .value("Date", chartItem.date),
y: .value("Amount", chartItem.amount)
)
LineMark(
x: .value("Date", chartItem.date),
y: .value("Amount", chartItem.amount)
)
.symbol() {
Button(action: {
print("chartItem: ", chartItem)
}, label: {
Circle()
.fill(color)
.frame (width: 15)
.opacity(0.6)
})
}
}
I need to touch LineMark symbol and get chartItem
. I tried to use Button
inside .symbol()
, but it don't works
Hot to add touch event to LineMark symbol?
CodePudding user response:
Try this simple approach, works well for me. It uses the chartOverlay
and a function tapSymbol
to calculate which symbol was tapped. The Circle
symbol change to green
when tapped.
import Foundation
import SwiftUI
import Charts
struct Measurement: Identifiable {
var id: String
var date: Double
var amount: Double
static let dx: CGFloat = 0.5 // <--- adjust as required
static let dy: CGFloat = 2.0 // <--- adjust as required
func isAround(x: CGFloat, y: CGFloat) -> Bool {
return date <= (x Measurement.dx) && date >= (x - Measurement.dx) && amount <= (y Measurement.dy) && amount >= (y - Measurement.dy)
}
}
struct ContentView: View {
let measurement = [
Measurement(id: "1", date: 2.0, amount: 11.0),
Measurement(id: "2", date: 4.0, amount: 22.0),
Measurement(id: "3", date: 6.0, amount: 38.0),
Measurement(id: "4", date: 8.0, amount: 45.0),
Measurement(id: "5", date: 10.0, amount: 30.0),
Measurement(id: "6", date: 12.0, amount: 57.0),
Measurement(id: "7", date: 14.0, amount: 26.0)
]
@State var selected = "0"
func tapSymbol(at location: CGPoint, proxy: ChartProxy, geometry: GeometryProxy) {
let xPos = location.x - geometry[proxy.plotAreaFrame].origin.x
let yPos = location.y - geometry[proxy.plotAreaFrame].origin.y
if let pos: (Double, Double) = proxy.value(at: CGPoint(x: xPos, y: yPos)) {
let results = measurement.filter{ $0.isAround(x: pos.0, y: pos.1) }
if let firstId = results.first?.id {
selected = firstId
}
}
}
var body: some View {
Chart {
ForEach(measurement) { chartItem in
AreaMark(
x: .value("Date", chartItem.date),
y: .value("Amount", chartItem.amount)
)
LineMark(
x: .value("Date", chartItem.date),
y: .value("Amount", chartItem.amount)
)
.symbol {
Circle().fill(selected == chartItem.id ? Color.green : Color.red).opacity(0.6).frame(width: 30)
}
}
}
.chartOverlay { proxy in
GeometryReader { geometry in
ZStack(alignment: .top) {
Rectangle().fill(.clear).contentShape(Rectangle())
.onTapGesture { location in
tapSymbol(at: location, proxy: proxy, geometry: geometry)
}
}
}
}
}
}