最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - Pinch and zoom data in a Chart - Stack Overflow

programmeradmin4浏览0评论

I am trying to figure out how to pinch and zoom (x-axis only) a plot (only the line, not the whole view) in a Chart. I can fake it using two slides, see my code below. It also includes a double-tap to zoom out and scrolling, and that works. But I cannot figure out how to convert a 'pinch' into a x-axis zoom of the data.

How can I update minXValue and maxXValue inside MagnifyGesture()?

let data: [DataPoint] = (Int(minGlobalX) ..< Int(maxGlobalX)).map { DataPoint(x: Double($0), y: Double(arc4random()) / Double(UInt32.max)) }

struct ContentView: View {
    @State private var minXValue = minGlobalX
    @State private var maxXValue = maxGlobalX

    var body: some View {
        VStack {
            Chart(data) {
                LineMark(
                    x: .value("x", $0.x),
                    y: .value("y", $0.y)
                )
                .lineStyle(StrokeStyle(lineWidth: 1))
            }
            .chartXScale(domain: minGlobalX...maxGlobalX)

            .onTapGesture(count: 2) {
                minXValue = minGlobalX
                maxXValue = maxGlobalX
            }
            .gesture(
                MagnifyGesture()
                     .onChanged { value in

                        minXValue = ?? // Here
                        maxXValue = ?? // Here
                    }
             )
            .padding(20)

            HStack(spacing: 20) {
                Text("Min: \(Int(minXValue))")
                Slider(value: $minXValue, in: minGlobalX ... maxXValue, step: 1)
            }
            .padding()

            HStack(spacing: 20) {
                Text("Max: \(Int(maxXValue))")
                Slider(value: $maxXValue, in: minXValue ... maxGlobalX, step: 1)
            }
            .padding()
        }
    }
}

I am trying to figure out how to pinch and zoom (x-axis only) a plot (only the line, not the whole view) in a Chart. I can fake it using two slides, see my code below. It also includes a double-tap to zoom out and scrolling, and that works. But I cannot figure out how to convert a 'pinch' into a x-axis zoom of the data.

How can I update minXValue and maxXValue inside MagnifyGesture()?

let data: [DataPoint] = (Int(minGlobalX) ..< Int(maxGlobalX)).map { DataPoint(x: Double($0), y: Double(arc4random()) / Double(UInt32.max)) }

struct ContentView: View {
    @State private var minXValue = minGlobalX
    @State private var maxXValue = maxGlobalX

    var body: some View {
        VStack {
            Chart(data) {
                LineMark(
                    x: .value("x", $0.x),
                    y: .value("y", $0.y)
                )
                .lineStyle(StrokeStyle(lineWidth: 1))
            }
            .chartXScale(domain: minGlobalX...maxGlobalX)

            .onTapGesture(count: 2) {
                minXValue = minGlobalX
                maxXValue = maxGlobalX
            }
            .gesture(
                MagnifyGesture()
                     .onChanged { value in

                        minXValue = ?? // Here
                        maxXValue = ?? // Here
                    }
             )
            .padding(20)

            HStack(spacing: 20) {
                Text("Min: \(Int(minXValue))")
                Slider(value: $minXValue, in: minGlobalX ... maxXValue, step: 1)
            }
            .padding()

            HStack(spacing: 20) {
                Text("Max: \(Int(maxXValue))")
                Slider(value: $maxXValue, in: minXValue ... maxGlobalX, step: 1)
            }
            .padding()
        }
    }
}
Share Improve this question edited Apr 5 at 11:37 koen asked Mar 15 at 13:37 koenkoen 5,7537 gold badges58 silver badges102 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I think I figured it out, inspired by Isn't there an easy way to pinch to zoom in an image in SwiftUI?

Here is the working code:

struct DataPoint: Identifiable {
    var id = UUID()
    let x: Double
    let y: Double
}

let minGlobalX: Double = 0
let maxGlobalX: Double = 100

let data: [DataPoint] = (Int(minGlobalX) ..< Int(maxGlobalX)).map { DataPoint(x: Double($0), y: Double(arc4random()) / Double(UInt32.max)) }

struct ContentView: View {
    @State private var minXValue = minGlobalX
    @State private var maxXValue = maxGlobalX

    @State var scale: CGFloat = 1.0
    @State var lastScaleValue: CGFloat = 1.0

    var magnification: some Gesture {
        MagnifyGesture()
            .onChanged { value in
                let delta = value.magnification / lastScaleValue
                lastScaleValue = value.magnification
                scale = scale * delta

                let globalWidth = maxGlobalX - minGlobalX
                let newWidth = 0.5 * (globalWidth - (globalWidth / scale))

                let newMinX = minGlobalX + newWidth
                if minGlobalX ... maxXValue ~= newMinX {
                    minXValue = newMinX
                }

                let newMaxX = maxGlobalX - newWidth
                if minXValue ... maxGlobalX ~= newMaxX {
                    maxXValue = newMaxX
                }
            }
            .onEnded { _ in
                lastScaleValue = 1.0
            }
    }

    var body: some View {
        VStack {
            Chart(data) {
                LineMark(
                    x: .value("x", $0.x),
                    y: .value("y", $0.y)
                )
                .lineStyle(StrokeStyle(lineWidth: 1))
            }
            .chartXScale(domain: minXValue ... maxXValue)

            .onTapGesture(count: 2) {
                minXValue = minGlobalX
                maxXValue = maxGlobalX
                scale = 1.0 // make sure to reset the scale here as well
            }
            .gesture(magnification)
            .padding(20)

            HStack(spacing: 20) {
                Text("Min: \(Int(minXValue))")
                Slider(value: $minXValue, in: minGlobalX ... maxXValue, step: 1)
            }
            .padding(.horizontal)

            HStack(spacing: 20) {
                Text("Max: \(Int(maxXValue))")
                Slider(value: $maxXValue, in: minXValue ... maxGlobalX, step: 1)
            }
            .padding(.horizontal)
        }
    }
}
发布评论

评论列表(0)

  1. 暂无评论