swiftUI:检测嵌入在数组中的两个视图的交点并交换它们的位置

swiftUI:检测嵌入在数组中的两个视图的交点并交换它们的位置,swift,swiftui,intersection,drag,Swift,Swiftui,Intersection,Drag,1) 同一视图的多个实例嵌入在一个数组中。主ContentView的子视图通过HStack中的ForEach显示它们。每个实例都有一个几何体读取器,用于在拖动过程中检测帧交点。目标是在检测到交叉点时交换视图在主内容视图上的位置。使用下面的代码,它根本无法检测交叉点 为什么? 是否有更好/更简单的方法(比几何体读取器)来检测视图的碰撞/交点 是否有比array+foreach更好的方法来分组和显示某个视图的动态实例数 var rects:[rect]=[rect(num:“0”)、rect(nu

1) 同一视图的多个实例嵌入在一个数组中。主ContentView的子视图通过HStack中的ForEach显示它们。每个实例都有一个几何体读取器,用于在拖动过程中检测帧交点。目标是在检测到交叉点时交换视图在主内容视图上的位置。使用下面的代码,它根本无法检测交叉点

  • 为什么?
  • 是否有更好/更简单的方法(比几何体读取器)来检测视图的碰撞/交点
  • 是否有比array+foreach更好的方法来分组和显示某个视图的动态实例数

    var rects:[rect]=[rect(num:“0”)、rect(num:“1”)、rect(num:“2”)]
    struct rect:View{
    设id=UUID()
    让num:String
    @状态变量viewFrame:CGRect=.0
    @状态变量viewOffset:CGSize=.0
    var body:一些观点{
    矩形()
    .笔划(颜色.蓝色,线宽:4)
    .框架(宽度:50,高度:100)
    .偏移(视图偏移)
    .background(文本(“\(num)”)
    .overlay(GeometryReader{r in
    颜色:红色
    .不透明度(0.5)
    奥纳佩尔先生{
    self.viewFrame=r.frame(在:。全局中)
    }
    })
    .手势(DragGesture(最小距离:0,坐标空间:。全局)
    .onChanged{中的值
    self.viewOffset=value.translation
    }
    .onEnded{中的值
    设newFrame=self.viewFrame.offsetBy(dx:self.viewOffset.width,dy:self.viewOffset.height)
    如果让swap_rect=rects.first(其中:{$0.id!=self.id&&$0.viewFrame.intersects(newFrame)}){
    打印(“相交”)
    swapAt(rects.firstIndex(of:self),rects.firstIndex(of:swaprect))
    }否则{
    打印(“否”)
    }
    self.viewOffset=.0
    }
    )
    }
    }
    结构字段:视图{
    var body:一些观点{
    HStack{
    ForEach(self.field\u env.rects,id:\.id){rect in
    直肠
    }
    }
    }
    }
    结构ContentView:View{
    var body:一些观点{
    VStack{
    字段()
    }
    }
    }
    
2) 我能够实现这一点的唯一方法是在environmentObject/ObservableObject的已发布数组变量中存储每个视图的附加GR碰撞帧。然而,一旦我的视图的两个实例交换了它们在主视图上的位置,GR碰撞帧CGRECT似乎没有得到更新(因为GR只有一个外观方法),下次我执行视图拖动和相交时,错误的视图被交换(不是视觉上碰撞的视图),例如,下面的代码显示了3个标记为0,1,2的矩形(作为它们在数组中的索引)。第一次将“2”拖到“1”上时,它们将正确交换,但如果将“1”拖到“2”上,“1”将与“0”交换

更新:

我曾尝试在通过通知中心执行交换后手动更新GR视图帧,因为我认为GR每次都使用初始帧位置(在.onAppear期间获得),交换后情况变得混乱,但问题似乎出在其他地方-交换的数组索引仍然错误(不是属于相交视图的视图):


Nathan,我不知道这会有多大帮助,但是,不管它值多少钱,你可以看看Paul Hudson创建的名为Switcheroo的Swift on Sundays项目。这是一个游戏,你可以从一个信盘中选择一个字母,然后从信盘中拖动一个字母来替换上面4个字母中的一个来生成一个新单词。我假设这是有一种可能对您有用的冲突。您可以在GiHub上找到该项目:

好的,最后我成功地使它工作了。我决定采用Paul的概念,将字符串存储在数组中,而不是完整的视图,并在ForEach循环期间使用这些stri实际创建视图在那之后,交换开始正常工作(即使用适当的索引).为了提高精度,我还将.intersects替换为.contains。因此,感谢Chrispy为我指明了正确的方向!

看来这次它工作正常…但是解决方案本身和开销代码量远远不够完美,不确定是否有更简单的方法-请看一看,欢迎所有想法

导入快捷界面
扩展通知。名称{
static let viewModified=Notification.Name(“视图已修改”)
}
类FieldEnv:observeObject{
@已发布的变量rect_帧:[CGRect]=[]
@已发布的变量rects:[Int]=[0,1,2]
}
struct rect:View{
@环境对象变量字段\环境:字段环境
让num:Int
@状态变量viewOffset:CGSize=.0
var body:一些观点{
矩形()
.笔划(颜色.蓝色,线宽:4)
.框架(宽度:50,高度:100)
.overlay(文本(“\(num)”)
.偏移(视图偏移)
.手势(DragGesture(最小距离:0,坐标空间:。全局)
.onChanged{拖入
self.viewOffset=拖动.translation
}
.onEnded{拖入
如果让swap_rect_i=self.field_env.rect_frames.firstIndex(其中:{$0.contains(drag.location)}){
打印(“相交”)
动画片{
self.field\u env.rects.swapAt(self.field\u env.rects.firstIndex(of:self.num)!,swap\u rect\u i)
self.viewOffset=.0
}
}否则{
打印(“否”)
self.viewOffset=.0
}
}
)
}
}
结构字段:视图{
@环境对象变量字段\环境:字段环境
设NC=NotificationCenter.default
var body:一些观点{
HStack{
法罗群岛
```swift
class FieldEnv: ObservableObject {
    @Published var rect_frames: [UUID:CGRect] = [:]
    @Published var rects: [rect] = [rect(num:"0"),rect(num:"1"),rect(num:"2")]
}

struct rect: View {
    @EnvironmentObject var field_env: FieldEnv
    let id = UUID()
    let num: String
    @State var viewFrame: CGRect = .zero
    @State var viewOffset: CGSize = .zero
    var body: some View {
        Rectangle()
        .stroke(Color.blue, lineWidth: 4)
        .frame(width: 50, height: 100)
        .offset(viewOffset)
        .background(Text("\(num)"))
        .overlay(GeometryReader { r in
            Color.red
                .opacity(0.5)
                .onAppear {
                    self.viewFrame = r.frame(in: .global)
                    self.field_env.rect_frames[self.id] = r.frame(in: .global)
            }
        })
        .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
            .onChanged { value in
                self.viewOffset = value.translation
        }
            .onEnded { value in
                let newFrame = self.viewFrame.offsetBy(dx: self.viewOffset.width, dy: self.viewOffset.height)
                if let swap_rect = self.field_env.rect_frames.first(where: {$0.key != self.id && $0.value.intersects(newFrame)}) {
                    print("intersects")
                    withAnimation {
                        self.field_env.rects.swapAt(self.field_env.rects.firstIndex(of: self)!, self.field_env.rects.firstIndex(where: {$0.id == swap_rect.key})!)
                    }
                } else {
                    print("nope")
                }
                self.viewOffset = .zero
        }
        )
    }
}

struct field: View {
    let NC = NotificationCenter.default
    @EnvironmentObject var field_env: FieldEnv
    var body: some View {
        HStack {
            ForEach(self.field_env.rects, id:\.id) { rect in
                    rect
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            field()
        }
    }
}
```
```swift
struct rect: View {
    @EnvironmentObject var field_env: FieldEnv
    let NC = NotificationCenter.default
    let id = UUID()
    let num: String
    @State var viewFrame: CGRect = .zero
    @State var viewOffset: CGSize = .zero
    var body: some View {
        Rectangle()
        .stroke(Color.blue, lineWidth: 4)
        .frame(width: 50, height: 100)
        .offset(viewOffset)
        .background(Text("\(num)"))
        .overlay(GeometryReader { r in
            Color.red
                .opacity(0.5)
                .onAppear {
                    self.viewFrame = r.frame(in: .global)
                    self.field_env.rect_frames[self.id] = r.frame(in: .global)
                    self.NC.addObserver(forName: .cardsSwapped, object: nil, queue: nil, using: {_ in self.field_env.rect_frames[self.id] = r.frame(in: .global)})
                }
        })
        .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
            .onChanged { value in
                self.viewOffset = value.translation
        }
            .onEnded { value in
                let newFrame = self.viewFrame.offsetBy(dx: self.viewOffset.width, dy: self.viewOffset.height)
                if let swap_rect = self.field_env.rect_frames.first(where: {$0.value.intersects(newFrame)}) {
                    print("intersects")
                    withAnimation {
                        self.field_env.rects.move(fromOffsets: [self.field_env.rects.firstIndex(of: self)!], toOffset: self.field_env.rects.firstIndex(where: {$0.id == swap_rect.key})!)
                    }
                } else {
                    print("nope")
                }
                self.viewOffset = .zero
                self.NC.post(name: .cardsSwapped, object: nil)
        }
        )
    }
}
```