You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI Chart中通过MagnifyGesture实现X轴捏合缩放

SwiftUI Chart折线X轴捏合缩放实现方法

要实现基于捏合手势的X轴缩放,核心是围绕手势中心点调整X轴范围,同时限制范围不超出全局数据边界。以下是具体实现步骤和修改后的代码:

关键思路

  • 用GeometryReader获取Chart的布局信息,将捏合手势的屏幕坐标转换为对应的X轴数据值(缩放中心点)
  • 根据捏合手势的缩放因子,计算新的X轴范围,保持缩放中心不变
  • 限制新的minXValue和maxXValue不超出minGlobalX和maxGlobalX,避免数据越界

修改后的完整代码

import SwiftUI
import Charts

// 假设全局X轴边界定义
let minGlobalX: Double = 0
let maxGlobalX: Double = 100

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

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 private var lastMagnification: CGFloat = 1.0

    var body: some View {
        VStack {
            GeometryReader { geo in
                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
                    lastMagnification = 1.0
                }
                .gesture(
                    MagnifyGesture()
                        .onChanged { value in
                            // 计算缩放变化量
                            let scaleChange = value.magnification / lastMagnification
                            lastMagnification = value.magnification
                            
                            // 获取手势中心点的屏幕坐标
                            let gestureCenter = value.location
                            // 将屏幕坐标转换为X轴数据值
                            let chartWidth = geo.size.width
                            let xRatio = (gestureCenter.x - geo.frame(in: .local).minX) / chartWidth
                            let currentRange = maxXValue - minXValue
                            let centerX = minXValue + xRatio * currentRange
                            
                            // 计算新的X轴范围,保持中心不变
                            let newRange = currentRange / Double(scaleChange)
                            var newMin = centerX - newRange / 2
                            var newMax = centerX + newRange / 2
                            
                            // 限制范围不超出全局边界
                            newMin = max(newMin, minGlobalX)
                            newMax = min(newMax, maxGlobalX)
                            // 避免min超过max(缩放过度时的保护)
                            if newMin >= newMax {
                                let mid = (newMin + newMax) / 2
                                newMin = mid - 1
                                newMax = mid + 1
                            }
                            
                            minXValue = newMin
                            maxXValue = newMax
                        }
                        .onEnded { _ in
                            // 手势结束后重置上次缩放因子,避免下次手势异常
                            lastMagnification = 1.0
                        }
                )
            }
            .padding(20)
            .frame(height: 300) // 给Chart设置固定高度,方便GeometryReader计算

            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()
        }
    }
}

代码说明

  1. GeometryReader:获取Chart的尺寸和位置,用于将手势的屏幕坐标转换为X轴数据值,确保缩放围绕用户捏合的中心点进行。
  2. 缩放因子处理:通过lastMagnification记录上一次的缩放比例,避免手势持续触发时范围被连续放大/缩小。
  3. 范围限制:计算新的minXValue和maxXValue后,强制限制在minGlobalX和maxGlobalX之间,同时防止min大于max的异常情况。
  4. 手势结束重置:在onEnded中重置lastMagnification,保证下一次捏合手势的计算准确性。

内容的提问来源于stack exchange,提问作者koen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:44:58