我可以使用 Swift Charts 框架绘制条形图。 现在我想在下面的条形图中执行 2 个操作。
我使用了点击手势,但点击点 2 区域不起作用。
import SwiftUI
import Charts
struct Item: Identifiable {
let id = UUID()
let month: String
let revenue: Double
let visits: Double
}
struct TapChart: View {
let items: [Item] = [
Item(month: "Jan", revenue: 100, visits: 2),
Item(month: "Feb", revenue: 200, visits: 1),
Item(month: "Mar", revenue: 150, visits: 4),
Item(month: "Apr", revenue: 130, visits: 0),
Item(month: "May", revenue: 300, visits: 3),
Item(month: "Jun", revenue: 50, visits: 2)
]
var myItems = [Item]()
var body: some View {
Chart {
ForEach(items) { items in
BarMark(
x: .value("Month", items.month),
y: .value("Revenue", items.revenue)
)
.foregroundStyle(Color.green.gradient)
}
}
.chartForegroundStyleScale([
"SEO Revenue" : Color(.green),
"Visits": Color(.purple)
])
.chartLegend(position: .top, alignment: .bottomTrailing)
.chartXAxis {
AxisMarks { axis in
AxisValueLabel(centered: true) {
Text("\(items[axis.index].month)")
.font(.system(size: 12, weight: .bold))
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(Color.red.opacity(0.3))
.cornerRadius(4)
.onTapGesture {
print("\(items[axis.index].month)")
}
}
}
}
.chartYAxis(.visible)
.padding()
.cornerRadius(15)
.frame(height: 300)
}
}
提前致谢。
我认为你必须用
chartOverlay
重新发明你自己的轴标记
.chartXAxis(.hidden)
.chartOverlay { proxy in
if let y = proxy.position(forY: 0.0) {
ForEach(items) { item in
if let x = proxy.position(forX: item.month) {
Text("\(item.month)")
.font(.system(size: 12, weight: .bold))
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(Color.red.opacity(0.3))
.cornerRadius(4)
.position(x: x, y: y + 40) // +40 to offset the label downwards a bit
.onTapGesture {
print("Tapped on label \(item.month)")
}
}
}
}
}
为了防止标签被绘图区域剪切,您还应该添加一些底部填充:
.chartPlotStyle { plot in
plot.padding(.bottom, 30)
}
至于检测栏上的点击,您可以执行以下操作:
.chartGesture{ proxy in
SpatialTapGesture().onEnded { value in
guard let x = proxy.value(atX: value.location.x, as: String.self),
let xRange = proxy.positionRange(forX: x) else { return }
let rangeWidth = xRange.upperBound - xRange.lowerBound
// assuming the bars occupy half of the available width
// i.e. BarMark(x: ..., y: ..., width: .ratio(0.5))
let barRatio = 0.5
let barRange = (xRange.lowerBound + rangeWidth * barRatio / 2)...(xRange.upperBound - rangeWidth * barRatio / 2)
guard barRange.contains(value.location.x),
let item = items.first(where: { $0.month == x }),
let y = proxy.position(forY: item.revenue),
let maxY = proxy.position(forY: 0.0),
value.location.y >= y && value.location.y <= maxY else {
return
}
print("Tapped on bar \(x)")
}
}