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

SwiftUI:如何让MagnificationGesture以两指中间的CGPoint为中心进行缩放

解决以两指中点为中心缩放的问题

你现在的代码只处理了缩放比例,但没有跟踪手势的中心位置,所以缩放始终以视图的默认中心(屏幕中心)为基准。要实现以两指放置的中点为中心缩放,我们需要同时管理缩放比例和视图偏移量,在手势更新时动态计算这两个值,补偿缩放带来的位置变化。

以下是修改后的完整代码:

struct ContentView: View {
    // 跟踪手势的临时数据:缩放比例和两指中点
    @GestureState private var gestureData = (scale: CGFloat(1), center: CGPoint.zero)
    // 持久化的缩放和偏移状态
    @State private var currentScale = CGFloat(1)
    @State private var currentOffset = CGPoint.zero
    // 手势开始时的初始状态,用于计算偏移量
    @State private var initialScale: CGFloat = 1
    @State private var initialOffset: CGPoint = .zero
    
    var body: some View {
        let gradient = Gradient(colors: [.red, .yellow, .green, .blue, .purple, .red, .yellow, .green, .blue, .purple, .red, .yellow, .green, .blue, .purple])
        
        let magnificationGesture = MagnificationGesture()
            // 更新手势状态,获取当前缩放比例和两指中点
            .updating($gestureData) { value, state, _ in
                state.scale = value
                state.center = value.location
            }
            .onChanged { value in
                // 计算当前的缩放比例(初始缩放 * 手势实时缩放)
                let newScale = initialScale * value
                // 计算缩放带来的偏移量变化:中点到视图中心的距离 * 缩放比例的变化率
                let deltaScale = newScale - initialScale
                let offsetDelta = CGPoint(
                    x: (gestureData.center.x - 1000) * deltaScale / initialScale,
                    y: (gestureData.center.y - 1000) * deltaScale / initialScale
                )
                // 更新当前偏移和缩放
                currentOffset = initialOffset + offsetDelta
                currentScale = newScale
            }
            .onEnded { value in
                // 手势结束后,保存当前状态作为下一次手势的初始值
                currentScale *= value
                initialScale = currentScale
                initialOffset = currentOffset
            }
        
        Circle()
            .fill(
                RadialGradient(gradient: gradient, center: .center, startRadius: 50, endRadius: 2000)
            )
            .frame(width: 2000, height: 2000)
            .offset(currentOffset)
            .scaleEffect(currentScale)
            .gesture(magnificationGesture.coordinateSpace(.named("CircleCoordinate")))
            .coordinateSpace(name: "CircleCoordinate")
            .onAppear {
                // 初始化初始状态
                initialScale = currentScale
                initialOffset = currentOffset
            }
    }
}

关键修改点说明:

  • 新增状态变量:

    • gestureData:用@GestureState跟踪手势的临时缩放比例和两指中点位置,手势结束后会自动重置。
    • currentScale/currentOffset:持久化存储当前的缩放比例和视图偏移量。
    • initialScale/initialOffset:记录手势开始时的状态,用于计算缩放过程中的偏移变化。
  • 手势逻辑调整:

    • 在updating闭包里获取手势的实时缩放比例和两指中点位置。
    • 在onChanged里计算新的缩放比例,并根据中点到视图中心的距离,计算需要补偿的偏移量——这样能保证缩放时中点位置保持不变。
    • onEnded里更新持久化的初始状态,确保下一次手势能基于当前状态继续操作。
  • 坐标系设置:

    • 给Circle视图添加自定义coordinateSpace,让手势的位置计算相对于Circle本身,而不是屏幕,这样中点坐标的计算更准确。

这样修改后,无论你在屏幕哪个位置用两指缩放,都会以两指的中点为中心进行缩放,完美解决你的问题!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:14:04