在UIViewController中,触摸UISegmentControl时如何让UITableView滚动?
解决UISegmentedControl拦截TableView滚动的问题
这个问题其实是因为UISegmentedControl会默认拦截水平滑动事件来切换分段,但它没法区分用户到底是想操作控件,还是想滚动下方的TableView。这里有个简单可靠的解决思路:自定义UISegmentedControl,根据滑动方向决定是否放弃事件传递。
实现步骤
- 创建自定义SegmentControl类,重写触摸追踪方法,判断滑动方向:
class PassThroughSegmentedControl: UISegmentedControl { private var initialTouchPoint: CGPoint = .zero // 记录初始触摸位置 override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { initialTouchPoint = touch.location(in: self) return super.beginTracking(touch, with: event) } // 追踪滑动过程,判断方向 override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { let currentPoint = touch.location(in: self) let verticalDistance = abs(currentPoint.y - initialTouchPoint.y) let horizontalDistance = abs(currentPoint.x - initialTouchPoint.x) // 如果垂直滑动距离大于水平,说明用户想滚动TableView,放弃事件 if verticalDistance > horizontalDistance { return false } // 否则继续处理Segment的自身逻辑(切换分段) return super.continueTracking(touch, with: event) } }
- 替换原有的UISegmentedControl:
- 如果是Storyboard/XIB创建的,直接把控件的Class改成
PassThroughSegmentedControl; - 如果是代码创建的,用
PassThroughSegmentedControl替代原有的UISegmentedControl初始化。
原理说明
当用户触摸SegmentControl时:
- 若点击或水平滑动(切换分段的操作),SegmentControl会正常响应自身逻辑;
- 若垂直滑动(滚动TableView的意图),SegmentControl会放弃事件追踪,让事件向下传递到TableView,触发滚动。
备选方案(手势代理)
如果你不想自定义控件,也可以通过给TableView添加手势并设置代理来实现:
class YourViewController: UIViewController, UIGestureRecognizerDelegate { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var headerView: UIView! override func viewDidLoad() { super.viewDidLoad() // 添加与TableView原生滚动手势一致的Pan手势 let panGesture = UIPanGestureRecognizer(target: tableView, action: #selector(UITableView.panGestureRecognizer(_:))) panGesture.delegate = self view.addGestureRecognizer(panGesture) } // 允许手势同时识别 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else { return false } let velocity = panGesture.velocity(in: view) // 垂直滑动时允许同时识别 return abs(velocity.y) > abs(velocity.x) } // 只有触摸点在SegmentControl上时才触发这个手势 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { let touchPoint = touch.location(in: headerView) return headerView.hitTest(touchPoint, with: nil) is UISegmentedControl } }
不过这个方案可能会有潜在的手势冲突问题,自定义控件的方法更稳定直观。
内容的提问来源于stack exchange,提问作者Sohel L.
相关产品推荐
相关产品推荐

