SwiftUI:如何在onLongPressGesture中获取列表项CGRect并存储于列表视图?
解决方案
可以通过PreferenceKey实现,在父视图(ContentView)中统一存储所有列表项的CGRect,并在长按触发时获取对应项的坐标,具体实现如下:
1. 定义偏好键传递坐标数据
首先创建一个自定义PreferenceKey,用来携带每个列表项的ID和对应的全局坐标:
struct ItemFramePreferenceKey: PreferenceKey { static var defaultValue: [Int: CGRect] = [:] static func reduce(value: inout [Int: CGRect], nextValue: () -> [Int: CGRect]) { value.merge(nextValue()) { $1 } } }
2. 修改ContentView实现逻辑
在原ContentView中添加状态变量存储所有项的坐标,通过GeometryReader捕获每个列表项的位置,并通过偏好键传递给父视图,最后在长按回调中根据ID获取对应坐标:
struct ContentView: View { // 存储每个列表项的ID和对应的CGRect @State private var itemFrames: [Int: CGRect] = [:] var body: some View { ScrollView { LazyVStack { ForEach((1...10), id: \.self) { itemId in Text("\(itemId)") .frame(height: 80) .frame(maxWidth: .infinity) .background(.yellow) // 捕获当前视图的全局坐标,通过偏好键传递给父视图 .background(GeometryReader { geo in Color.clear .preference( key: ItemFramePreferenceKey.self, value: [itemId: geo.frame(in: .global)] ) }) .onLongPressGesture { // 根据当前item的ID取出对应的CGRect if let rect = itemFrames[itemId] { print("长按项\(itemId)的坐标:\(rect)") // 在这里使用rect做后续操作 } } } } // 监听偏好键变化,更新itemFrames .onPreferenceChange(ItemFramePreferenceKey.self) { newFrames in itemFrames = newFrames } } } }
说明
PreferenceKey是SwiftUI中父子视图传递数据的常用方式,这里用来将每个列表项的坐标统一收集到父视图的itemFrames字典中。GeometryReader的frame(in: .global)会返回视图在屏幕坐标系中的位置和大小,确保拿到的是全局坐标。- 由于使用了
LazyVStack,列表项会按需加载,PreferenceKey的reduce方法会自动合并新出现的项的坐标,保证itemFrames始终是最新的。
内容的提问来源于stack exchange,提问作者sash
相关产品推荐
相关产品推荐

