Я изучаю диаграммы SwiftUI с датами по оси X и плавающими числами по оси Y. Я слежу за некоторыми видео и руководствами, но ни одно из них не сталкивается с моей проблемой. Проблема заключается в том, что при нажатии на крайние случаи гистограммы аннотация не помещается в рамку и начинает перемещать столбцы, что неправильно. Проверьте гифку, чтобы увидеть это. Ваша помощь приветствуется :)
struct BarChartView: View {
struct BarData: Identifiable, Equatable {
var type: String
var date: Date
var count: Float
var id: Int
}
var data: [BarData] = [BarData(type: "test1", date: Date(), count: 10.0, id: 1)
//and more]
@State var activeItem: BarData?
var body: some View {
NavigationStack{
VStack(alignment : .leading, spacing: 15) {
Text("testing errors")
VStack{
Chart(data) { item in
BarMark(
x: .value("Date", item.date, unit: .day),
y: .value("Sum", item.count )
)
if let activeItem,activeItem.id == item.id{
RuleMark(x: .value("Day", activeItem.date))
.lineStyle(.init(lineWidth: 2, miterLimit: 2, dash: [2], dashPhase: 5))
.annotation(position: .top){
VStack(alignment: .leading, spacing: 6){
HStack{
Text("Type")
.font(.caption)
.foregroundColor(.gray)
Text(activeItem.type)
.font(.title3.bold())
}
HStack{
Text("Sum")
.font(.caption)
.foregroundColor(.gray)
Text("\(activeItem.count, specifier: "%.2f")")
.font(.title3.bold())
}
}
.padding(.horizontal, 10)
.padding(.vertical, 4)
.background(RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(.black.shadow(.drop(radius: 2))))
}
}
}
.chartOverlay {
// getting activeItem
}
.chartYScale(domain: 0...50)
.frame(height: 250)
}
.padding()
.background{
RoundedRectangle(cornerRadius: 10, style: .continuous)
.stroke(.gray, lineWidth: 1)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.padding(.top, 10)
.navigationTitle("Test charts")
}
}
}





Вы можете поместить аннотацию по разные стороны вертикальной линии, в зависимости от того, где в массиве данных находится активный элемент.
Простая реализация — поместить его справа от линии, когда активный элемент находится в левой части диаграммы, и слева от линии, когда активный элемент находится в правой части диаграммы.
var annotationPosition: AnnotationPosition {
guard let activeItem else { return .automatic }
if let index = data.firstIndex(of: activeItem), index < data.count / 2 {
return .topTrailing
} else {
return .topLeading
}
}
.annotation(position: annotationPosition) { ... }
Вы также можете добавить, чтобы аннотации к данным посередине (там, где находится «середина»), имели позицию .top.
Альтернативно, добавьте к полосам отступы, чтобы для аннотаций было достаточно места и чтобы не приходилось сдвигать полосы в соответствии с аннотациями.
.chartXScale(range: .plotDimension(padding: 30))
Теперь (iOS 17.0+) можно создать аннотацию, указав ее AnnotationOverflowResolution. API
.annotation(
position: /* getAnnotationPosition() */,
alignment: .center,
spacing: 0,
overflowResolution: AnnotationOverflowResolution(x: .disabled, y: .disabled)
) {
Помимо динамического расчета положения аннотации, вы можете отключить разрешение переполнения, чтобы аннотации вообще не перемещали диаграмму. (например, когда ваш текст/перевод имеет динамическую длину и может не поместиться в длинную сторону экрана)