Показать правильное положение на оси Y наложения диаграммы

У меня есть следующая диаграмма SwiftUI. Я пытаюсь получить значение y в зависимости от того, куда перетаскивает пользователь, чтобы я мог расположить Capsule в правильном положении на диаграмме. К сожалению, я не могу получить правильное положение y, но вместо этого этот код показывает мне, куда пользователь перетаскивает график, но не удерживает индикатор на линии графика.

Может ли кто-нибудь подсказать, где я ошибаюсь. Вот мой код. И если что-то неясно, дайте мне знать, и я смогу предоставить более подробную информацию.

Я думала этоvalue(atY:as:) может помочь, но тоже не сработало.

struct ContentView: View {
    @State private var numbers = (0...10).map { _ in
        Int.random(in: 0...10)
    }
    
    @State private var indicatorIndex = 0
    @State private var indicatorNumber = 0.0
    @State private var indicatorLocation = CGPointMake(0, 0)
    
    var body: some View {
        Chart {
            ForEach(Array(zip(numbers, numbers.indices)), id: \.0) { number, index in
                LineMark(
                    x: .value("Index", index),
                    y: .value("Value", number)
                )
            }
        }
        .chartOverlay { proxy in
            GeometryReader { geometry in
                Capsule()
                    .strokeBorder(Color.red, lineWidth: 1.0)
                    .background(Color.blue)
                    .frame(width: 80, height: 20)
                    .overlay {
                        HStack {
                            Text("\(indicatorIndex)")
                                .font(.system(size: 12.0))
                                .fontWeight(.semibold)
                                .foregroundStyle(.red)
                            Text("\(indicatorNumber)")
                                .font(.system(size: 12.0))
                                .fontWeight(.semibold)
                                .foregroundStyle(.white)
                            
                        }
                       
                    }
                    .offset(x: indicatorLocation.x, y: indicatorLocation.y)
                Rectangle().fill(.clear).contentShape(Rectangle())
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                // Convert the gesture location to the coordinate space of the plot area.
                                let origin = geometry[proxy.plotAreaFrame].origin
                                let location = CGPoint(
                                    x: value.location.x - origin.x,
                                    y: value.location.y - origin.y
                                )
                                
                                // Get the x (date) and y (price) value from the location.
                                let (index, number) = proxy.value(at: location, as: (Int, Double).self) ?? (0, 0)
                                let test = proxy.value(atY: origin.x, as: Double.self)
                                indicatorIndex = index
                                indicatorNumber = number
                                indicatorLocation = CGPoint(x: value.location.x, y: test ?? 0.0)
                                print("Location: \(number) - number, \(index) - index , \(test) test")
                            }
                    )
            }
        }
        
    }
}

Вот несколько скриншотов, показывающих, где находится капсула и где я хочу ее разместить.


1
70
1

Ответ:

Решено

ChartProxy не имеет абсолютно никакого представления о данных, отображенных на диаграмме. Вы должны найти координаты y линии, используя свой источник данных, то есть массив numbers.

// get the x value of the tapped location
let x = proxy.value(atX: location.x, as: Double.self) ?? 0

// get which two plotted points is the tapped location between
let (lowerIndex, upperIndex) = (x.rounded(.down), x.rounded(.up))

// get the y values for the above x values
// these indices might be out of range - you should decide what to do in that case
let (lowerNumber, upperNumber) = (numbers[Int(lowerIndex)], numbers[Int(upperIndex)])

// now convert these to y *coordinates*
let (lowerY, upperY) = (proxy.position(forY: lowerNumber) ?? 0, proxy.position(forY: upperNumber) ?? 0)

// finally, linear interpolation
func lerp(_ a: Double, _ b: Double, _ x: Double) -> Double {
    a + (b - a) * x
}
let y = lerp(lowerY, upperY, x - lowerIndex)

indicatorLocation = CGPoint(x: value.location.x, y: y)

В данном случае это относительно просто, поскольку вы строите график каждого индекса массива, а интерполяция является линейной. В общем, это может оказаться очень сложным. В любом случае общая идея та же (см. комментарии к коду).