如何避免父SwiftUI视图的onTapGesture拦截UIKit按钮的点击事件?
问题:UIKit按钮封装在UIViewRepresentable后,父SwiftUI视图的onTapGesture导致按钮无法响应
当把UIKit按钮封装到UIViewRepresentable中,且父SwiftUI视图添加了onTapGesture时,会出现UIKit按钮无法响应点击的情况。原因是SwiftUI的onTapGesture会在触摸事件到达UIKit按钮前捕获事件,阻止按钮检测点击。
提供的代码如下:
UIViewRepresentable封装代码
import SwiftUI import UIKit struct MywebView: UIViewRepresentable { @Binding var showUIView: Bool func makeUIView(context: Context) -> UIButton { let button = UIButton() button.setTitle("dismiss UIView", for: .normal) button.setTitleColor(.blue, for: .normal) button.backgroundColor = .red button.addTarget(context.coordinator, action: #selector(Coordinator.dismissUIView), for: .touchUpInside) return button } func updateUIView(_ uiView: UIViewType, context: Context) { // 空实现 } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UIGestureRecognizerDelegate { var parent: MywebView init(_ parent: MywebView) { self.parent = parent } @objc func dismissUIView() { print("dismiss button pressed") parent.showUIView = false } } }
SwiftUI父视图代码
import SwiftUI struct ContentView: View { @State var showUiView: Bool = false var body: some View { VStack(spacing: 40) { Button("ShowUIView") { showUiView.toggle() } if showUiView { MywebView(showUIView: $showUiView) .frame(width: 100, height: 60) } } .onTapGesture { print("tapped on swiftUI view") } } } #Preview { ContentView() // .preferredColorScheme(.dark) }
解决方案
方案1:给UIViewRepresentable添加高优先级占位手势
在SwiftUI中使用MywebView时,添加一个高优先级的空Tap手势,让它优先捕获按钮区域的点击事件,既不干扰UIKit按钮的响应,又能阻止父视图的onTapGesture触发:
if showUiView { MywebView(showUIView: $showUiView) .frame(width: 100, height: 60) .highPriorityGesture(TapGesture(), including: .subviews) }
原理:highPriorityGesture会让该手势的优先级高于父视图的手势,当点击按钮区域时,这个空手势会先被触发,但因为没有实现逻辑,触摸事件会传递给内部的UIKit按钮,同时父视图的onTapGesture不会被触发。
方案2:通过UIKit手势代理优先响应按钮事件
利用已实现的Coordinator作为手势代理,让UIKit按钮的手势识别器优先于SwiftUI父视图的手势:
- 修改
makeUIView方法,给按钮的手势识别器设置代理:
func makeUIView(context: Context) -> UIButton { let button = UIButton() button.setTitle("dismiss UIView", for: .normal) button.setTitleColor(.blue, for: .normal) button.backgroundColor = .red button.addTarget(context.coordinator, action: #selector(Coordinator.dismissUIView), for: .touchUpInside) // 给按钮的所有手势识别器设置代理 button.gestureRecognizers?.forEach { $0.delegate = context.coordinator } return button }
- 在
Coordinator中添加手势代理方法,阻止手势同时识别:
class Coordinator: NSObject, UIGestureRecognizerDelegate { var parent: MywebView init(_ parent: MywebView) { self.parent = parent } @objc func dismissUIView() { print("dismiss button pressed") parent.showUIView = false } // 禁止按钮手势与其他手势同时识别,确保按钮优先响应 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return false } }
原理:通过代理方法明确禁止按钮手势和父视图的SwiftUI手势同时识别,UIKit按钮的手势会优先处理触摸事件,父视图的onTapGesture就不会被触发。
内容的提问来源于stack exchange,提问作者Beast
相关产品推荐
相关产品推荐

