SwiftUI中List添加minimumDistance:0.0的simultaneousGesture后点击失效求助
解决方案
问题根源
你之前用DragGesture(minimumDistance: 0.0)的方式会拦截所有触摸事件(包括点击),导致列表行内的onTapGesture和Button无法响应;而设为0.1时,点击事件不会触发拖拽手势,但滚动位移超过阈值后依然会被List的原生滚动手势处理,所以无法禁用滚动。
推荐方案:利用List底层的UITableView禁用滚动(iOS15兼容)
SwiftUI的List在iOS底层基于UITableView实现,直接禁用UITableView的滚动属性是最可靠的方式,既不会影响行内交互,又能彻底禁用滚动。
方法1:使用Introspect库(简洁高效)
Introspect是一个常用的SwiftUI工具库,可直接获取底层UIKit组件。
- 通过Swift Package Manager或CocoaPods引入Introspect库
- 修改代码如下:
import SwiftUI import Introspect struct ContentView: View { var body: some View { ZStack { List { ForEach(0..<10, id:\.self) { index in VStack { Text("Sample text \(index)") .onTapGesture { print("Sample Text tapped \(index)") } Button("Sample button \(index)") { print("Sample button tapped \(index)") } .buttonStyle(.bordered) .frame(height: 50) } .frame(height: 100) } } // 获取底层UITableView并禁用滚动 .introspectTableView { tableView in tableView.isScrollEnabled = false } } } }
方法2:自定义UIViewRepresentable(无第三方依赖)
如果不想引入第三方库,可以自行封装基于UITableView的非滚动列表:
import SwiftUI // 自定义非滚动列表组件 struct NonScrollableList<Item: Identifiable, Content: View>: UIViewRepresentable { let items: [Item] let content: (Item) -> Content func makeUIView(context: Context) -> UITableView { let tableView = UITableView() tableView.isScrollEnabled = false tableView.delegate = context.coordinator tableView.dataSource = context.coordinator tableView.register(HostingTableViewCell<Content>.self, forCellReuseIdentifier: "Cell") tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 return tableView } func updateUIView(_ uiView: UITableView, context: Context) { context.coordinator.items = items uiView.reloadData() } func makeCoordinator() -> Coordinator { Coordinator(content: content) } class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource { var items: [Item] = [] let content: (Item) -> Content init(content: @escaping (Item) -> Content) { self.content = content } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? HostingTableViewCell<Content> else { return UITableViewCell() } let item = items[indexPath.row] cell.hostingController.rootView = content(item) return cell } } } // 承载SwiftUI视图的UITableViewCell class HostingTableViewCell<Content: View>: UITableViewCell { let hostingController = UIHostingController<Content>(rootView: EmptyView() as! Content) override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupHostingView() } required init?(coder: NSCoder) { super.init(coder: coder) setupHostingView() } private func setupHostingView() { contentView.addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } } // 使用示例 struct ContentView: View { struct Item: Identifiable { let id: Int let value: Int } let items = Array(0..<10).map { Item(id: $0, value: $0) } var body: some View { ZStack { NonScrollableList(items: items) { item in VStack { Text("Sample text \(item.value)") .onTapGesture { print("Sample Text tapped \(item.value)") } Button("Sample button \(item.value)") { print("Sample button tapped \(item.value)") } .buttonStyle(.bordered) .frame(height: 50) } .frame(height: 100) } } } }
内容的提问来源于stack exchange,提问作者Sajjad Sarkoobi
相关产品推荐
相关产品推荐

