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

问题描述 投票:0回答:3

1]相同的视图的许多实例被嵌入到一个数组中。主ContentView的子视图通过HStack中的ForEach显示它们。每个实例都有一个几何读取器,以检测拖动期间的框架相交。目标是在检测到相交时交换视图在主内容视图上的位置。使用下面的代码,它只是没有检测到交叉点。

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

    var rects: [rect] = [rect(num:"0"),rect(num:"1"),rect(num:"2")]
    
    struct rect: View {
        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)
                }
            })
            .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 = rects.first(where: {$0.id != self.id && $0.viewFrame.intersects(newFrame)}) {
                        print("intersects")
                        rects.swapAt(rects.firstIndex(of: self), rects.firstIndex(of: swap_rect))
                    } else {
                        print("nope")
                    }
                    self.viewOffset = .zero
            }
            )
        }
    }
    
    struct field: View {
        var body: some View {
            HStack {
                ForEach(self.field_env.rects, id:\.id) { rect in
                        rect
                }
            }
        }
    }
    
    struct ContentView: View {
        var body: some View {
            VStack {
                field()
            }
        }
    }
    

2]我能够做到这一点的唯一方法是通过将每个视图的其他GR冲突帧存储在environmentObject / ObservableObject的已发布数组var中。但是,一旦视图的两个实例交换了它们在主视图上的位置,似乎GR碰撞框架CGRects不会得到更新(因为GR仅具有onappear方法),并且下次我执行视图拖动和相交时,错误的视图将被交换(而不是视觉上相撞的)下面的代码显示3个标记为0,1,2的矩形(作为它们在数组中的索引)。第一次将“ 2”拖到“ 1”上时,它们可以正确交换,但是如果将“ 1”拖到“ 2”上,则“ 1”将被替换为“ 0”。```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() } } } ```

更新:

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

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

1]相同的视图的许多实例被嵌入到一个数组中。主ContentView的子视图通过HStack中的ForEach显示它们。每个实例都有一个几何读取器以检测框架...
swift swiftui intersection drag
3个回答
0
投票
Nathan,我不确定这可能有多大帮助,但是,就其价值而言,您可以看看Paul Hudson创建的名为Switcheroo的Swift on Sundays项目。这是一个游戏,您可以从中选择一个字母托盘,然后从托盘中拖动一个字母以替换上面的4个字母之一,以制成一个新单词。我假设这会产生某种冲突,可能对您有用。您可以在GiHub上找到该项目:https://github.com/twostraws/SwiftOnSundays

0
投票
好吧,我终于设法使它工作了。我已经决定采用Paul的概念,将

strings


0
投票
看起来这一次运行正常……但是解决方案本身和开销代码远非完美,不确定不确定是否有更简单的方法-请看一下,欢迎所有想法
© www.soinside.com 2019 - 2024. All rights reserved.