Reputation: 30618
I got a LazyVGrid, and I would like to know is this possible to return which griditem by given a gesture.location. Thanks.
Upvotes: 1
Views: 182
Reputation: 53181
To store the location of each item in a LazyVGrid
, do this using PreferenceKey
:
First define an object to store the frame for each view in the grid:
struct ModelFrame: Hashable {
let id: String
let frame: CGRect
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
then create a PreferenceKey
as follows:
struct ModelFrameKey: PreferenceKey {
static var defaultValue: Set<ModelFrame> = []
static func reduce(value: inout Set<ModelFrame>, nextValue: () -> Set<ModelFrame>) {
value = value.union(nextValue())
}
}
Then for each view in the grid, add a clear background wrapped in a GeometryReader
with .preference
modifier to store the frame for the view.
struct Model: Identifiable {
var id: String { name }
let name: String
}
struct ContentView: View {
let models = ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu"].map(Model.init)
@State private var modelFrames: Set<ModelFrame> = []
@State private var overModel: Model?
var body: some View {
VStack {
Text("Over: \(overModel?.name ?? "nothing")")
.font(.title)
LazyVGrid(columns: [GridItem(), GridItem()]) {
ForEach(models) { model in
Text(model.name)
.padding()
.background(overModel?.id == model.id ? .red : .yellow, in: RoundedRectangle(cornerRadius: 8))
.background {
GeometryReader { proxy in
Color.clear.preference(key: ModelFrameKey.self, value: [ModelFrame(id: model.id, frame: proxy.frame(in: .named("Grid")))])
}
}
}
}
.coordinateSpace(name: "Grid")
.onPreferenceChange(ModelFrameKey.self) { frames in
self.modelFrames = frames
}
.gesture(
DragGesture()
.onChanged { value in
overModel = model(containing: value.location)
}
)
}
}
func model(containing point: CGPoint) -> Model? {
guard let id = modelFrames.first(where: { $0.frame.contains(point) })?.id else {
return nil
}
return models.first(where: { $0.id == id })
}
Now you have your frames, you can use CGRect.contains(CGPoint)
in your drag gesture to see which view contains the current location.
Upvotes: 2