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

如何通过DragGesture单独偏移每个Shape?

如何通过DragGesture单独偏移每个Shape?

嘿,我一眼就看出问题所在啦——你现在所有的星星都共用同一个off偏移量,不管拖动哪一个,都会修改这个全局变量,自然所有星星都会一起动啦。要让每个星星能单独拖动,我们得给每个星星分配独立的偏移状态,还要处理拖动结束后的位置保存,不然松开手星星会弹回去哦~

解决方案步骤:

  1. 创建独立的模型结构体:给每个星星封装自己的形状和偏移量,还要让它遵守Identifiable,这样ForEach能更安全地识别每个元素。
  2. 替换全局偏移为模型内的偏移:把原来的全局off换成每个模型里的持久化偏移量,再用GestureState处理拖动时的临时位移。
  3. 修改手势逻辑:拖动时只更新当前星星的偏移,结束后保存最终位置。

修改后的完整代码:

import SwiftUI

// 定义带独立状态的星星模型
struct StarItem: Identifiable {
    let id = UUID()
    let shape: any Shape
    // 保存拖动后的最终偏移位置
    var persistentOffset: CGSize = .zero
}

struct ContentView: View {
    @State var arrayOfStars: [StarItem] = []
    // 临时保存拖动过程中的位移,手势结束后自动重置
    @GestureState private var tempDragOffset: CGSize = .zero
    
    var body: some View {
        ZStack {
            HStack {
                Button {
                    // 添加新的带独立状态的星星
                    arrayOfStars.append(StarItem(shape: Star()))
                } label: {
                    Text("Add Star").foregroundColor(.red)
                }
            }
            
            ForEach($arrayOfStars) { $item in
                item.shape
                    .frame(width: 25)
                    .offset(x: -100)
                    // 初始排列位置(保留你原来的逻辑)
                    .offset(x: 20 + CGFloat(arrayOfStars.firstIndex(where: { $0.id == item.id })! * 42), y: -100)
                    .foregroundColor(.red)
                    // 当前位置 = 持久化偏移 + 临时拖动位移
                    .offset(item.persistentOffset + tempDragOffset)
                    .gesture(
                        DragGesture()
                            // 实时更新临时拖动位移
                            .updating($tempDragOffset) { value, state, _ in
                                state = value.translation
                            }
                            // 拖动结束后,把临时位移加到持久化偏移里,保存位置
                            .onEnded { value in
                                item.persistentOffset += value.translation
                            }
                    )
            }
        }
    }
}

// 假设你的Star形状实现(如果原代码已有可直接替换)
struct Star: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2
        let points = (0..<5).map { i -> CGPoint in
            let angle = Double(i) * .pi * 2 / 5 - .pi / 2
            let x = center.x + CGFloat(cos(angle)) * radius
            let y = center.y + CGFloat(sin(angle)) * radius
            return CGPoint(x: x, y: y)
        }
        path.move(to: points[0])
        path.addLine(to: points[2])
        path.addLine(to: points[4])
        path.addLine(to: points[1])
        path.addLine(to: points[3])
        path.closeSubpath()
        return path
    }
}

关键说明:

  • StarItem结构体给每个星星分配了唯一的id和独立的persistentOffset,这样每个星星的位置都是单独管理的,不会互相干扰。
  • @GestureState修饰的tempDragOffset用来临时保存拖动过程中的位移,手势结束后会自动重置,不会污染其他星星的状态。
  • 拖动结束时,我们把临时位移加到persistentOffset里,这样星星会保持在拖动后的位置,不会弹回初始状态。

这样修改后,你点击添加多个星星,每个都能单独拖动啦~

备注:内容来源于stack exchange,提问作者Kurt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 13:53:02