具有很多Button的VStack上的DragGesture,如何检测拖动何时在Button内部

时间:2019-11-17 12:14:19

标签: ios swiftui

在UIKit中,可以使用以下代码完成此操作:

if button.frame.contains(sender.location(in: rightStackView)) { ... }

但是在SwiftUI中,我似乎找不到与frame.contains类似的东西, 那么如何确定拖动在特定按钮或其他视图中的时间?

2 个答案:

答案 0 :(得分:1)

好吧,这是很多代码,所以我尽可能地简化了它,以演示可能的方法(不包括帧重叠,拖动重定位,浮动拖动项等)。此外,从问题上不清楚将使用什么。无论如何,希望该演示会有所帮助。

注意:使用过Xcode 11.2

这是结果

SwiftUI drag destination frame detection

这是预览提供程序的一个模块演示代码

import SwiftUI

struct DestinationDataKey: PreferenceKey {
    typealias Value = [DestinationData]

    static var defaultValue: [DestinationData] = []

    static func reduce(value: inout [DestinationData], nextValue: () -> [DestinationData]) {
        value.append(contentsOf: nextValue())
    }
}

struct DestinationData: Equatable {
    let destination: Int
    let frame: CGRect
}

struct DestinationDataSetter: View {
    let destination: Int

    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(key: DestinationDataKey.self,
                            value: [DestinationData(destination: self.destination, frame: geometry.frame(in: .global))])
        }
    }
}

struct DestinationView: View {
    @Binding var active: Int
    let label: String
    let id: Int

    var body: some View {
        Button(action: {}, label: {
            Text(label).padding(10).background(self.active == id ? Color.red : Color.green)
        })
        .background(DestinationDataSetter(destination: id))
    }
}

struct TestDragging: View {
    @State var active = 0
    @State var destinations: [Int: CGRect] = [:]

    var body: some View {
        VStack {
            Text("Drag From Here").padding().background(Color.yellow)
                .gesture(DragGesture(minimumDistance: 0.1, coordinateSpace: .global)
                    .onChanged { value in
                        self.active = 0
                        for (id, frame) in self.destinations {
                            if frame.contains(value.location) {
                                self.active = id
                            }
                        }
                    }
                    .onEnded { value in
                        // do something on drop
                        self.active = 0
                    }
            )
            Divider()
            DestinationView(active: $active, label: "Drag Over Me", id: 1)
        }.onPreferenceChange(DestinationDataKey.self) { preferences in
            for p in preferences {
                self.destinations[p.destination] = p.frame
            }
        }
    }
}

struct TestDragging_Previews: PreviewProvider {
    static var previews: some View {
        TestDragging()
    }
}

答案 1 :(得分:1)

这似乎与您想要实现的目标非常接近:
这来自here
(对不起,这个答案没有适应你的具体情况。我刚刚在做类似的事情,想分享一下)

struct ContentView: View {
    @State private var frames = [CGRect]()
    @State private var states = [false, false, false]
    
    let values = ["First", "Second", "Third"]

    var body: some View {

        // This came from https://www.youtube.com/watch?v=ffV_fYcFoX0&t=6175s
        HStack {
            ForEach(0 ..< values.count) { index in
                Text(values[index])
                    .overlay(
                        // Creating an overlay creates a view that matches the size of the original view
                        GeometryReader { geo in
                            Color.clear
                                .onAppear {
                                    // Insert that into a state array, and now can access the coordinates of frames
                                    frames.insert((geo.frame(in: .global)), at: 0)
                                }
                        }
                    )
            }
            .gesture(
                DragGesture(minimumDistance: 0, coordinateSpace: .global)
                        .onChanged { value in

                            // If there's a match activate the view by toggling state
                            if let match = frames.firstIndex(where: { $0.contains(value.location) }) {
                                states[match] = true
                            }
                        }
                        .onEnded { _ in
                        }
            )
        }
    }
}