UIScrollView覆盖UIButtons致点击无效,自定义PassThroughView方案咨询
解决方案分析:UIScrollView遮挡按钮点击的问题
你的PassThroughView方案完全可行!
这个思路的核心是让容器视图把点击事件透传给下方的按钮——通过重写pointInside(_:with:)方法,判断当前点击点是否落在按钮范围内:如果是,就返回false(告诉系统这个视图不拦截该点击,让事件继续向下传递);否则返回true(让内部的ScrollView正常处理点击/滚动)。
下面是完整的代码实现示例:
class PassThroughView: UIView { // 存储需要穿透点击的按钮(或其他视图) var passThroughViews: [UIView] = [] override func pointInside(_ point: CGPoint, with event: UIEvent?) -> Bool { // 遍历所有需要穿透的视图,判断点击点是否在它们的范围内 for view in passThroughViews { // 将当前视图坐标系的点转换到目标视图的坐标系 let convertedPoint = convert(point, to: view) if view.pointInside(convertedPoint, with: event) { // 点击点在目标视图上,不拦截事件,让事件传递下去 return false } } // 点击点不在需要穿透的视图上,正常拦截事件(让ScrollView处理) return super.pointInside(point, with: event) } }
使用时只需把UIScrollView放到这个容器里,再把两个按钮加入passThroughViews数组即可:
// 假设已经创建了button1、button2、scrollView let passThroughContainer = PassThroughView() passThroughContainer.addSubview(scrollView) // 设置scrollView约束固定到容器边缘 scrollView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: passThroughContainer.topAnchor), scrollView.leadingAnchor.constraint(equalTo: passThroughContainer.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: passThroughContainer.trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: passThroughContainer.bottomAnchor) ]) // 添加需要穿透的按钮 passThroughContainer.passThroughViews = [button1, button2] // 最后把passThroughContainer添加到父视图中
更优的简化思路:直接子类化UIScrollView
其实你不需要额外的容器视图,直接子类化UIScrollView,重写它的hitTest(_:with:)方法,让ScrollView自己判断是否要拦截点击事件,代码更紧凑:
class PassThroughScrollView: UIScrollView { var passThroughViews: [UIView] = [] override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // 先判断点击点是否在需要穿透的视图范围内 for view in passThroughViews { let convertedPoint = convert(point, to: view) if view.pointInside(convertedPoint, with: event) { // 返回目标视图,让它处理点击事件 return view.hitTest(convertedPoint, with: event) } } // 不在穿透视图上,正常处理ScrollView的点击/滚动 return super.hitTest(point, with: event) } }
使用时直接用这个子类代替原生UIScrollView,再把按钮加入passThroughViews数组:
let scrollView = PassThroughScrollView() scrollView.passThroughViews = [button1, button2] // 正常设置约束和添加到父视图即可
这个方案省去了多余的视图层级,逻辑更直接——让ScrollView自己决定是否把事件让给下方的按钮。
补充小提示
- 如果按钮是动态创建的,记得在按钮添加到视图后及时更新
passThroughViews数组; - 确保
passThroughViews里的视图和ScrollView能正确转换坐标系(一般在同一父视图层级下就没问题); - 如果ScrollView不需要滚动功能,也可以直接设置
scrollView.isUserInteractionEnabled = false,但这样ScrollView的所有交互都会失效,只适合特殊场景。
内容的提问来源于stack exchange,提问作者ELKA
相关产品推荐
相关产品推荐

