Swift 4:如何将SegmentedControl的平移手势传递给TableView?
解决SegmentedControl区域滑动TableView的问题
嘿,我之前也碰到过这个需求!你现在的问题主要是自定义Pan手势和TableView原生滚动手势的冲突,而且手动处理平移时没有完全模拟原生滚动的行为(比如惯性、边界限制)。下面给你两个经过验证的解决方案,按需选择:
方案一:完全模拟原生滚动(推荐)
这个方案能让SegmentedControl区域的滑动体验和TableView其他区域完全一致,包括惯性滚动和边界回弹。
步骤1:设置手势代理
首先让你的ViewController遵守UIGestureRecognizerDelegate协议,然后给自定义的Pan手势设置代理:
class YourViewController: UIViewController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) // 设置代理 panGestureRecognizer.delegate = self segmentedControl.addGestureRecognizer(panGestureRecognizer) } }
步骤2:允许手势同时识别
实现代理方法,让自定义Pan手势和TableView的滚动手势可以同时工作:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 只允许和TableView的滚动手势同时识别 return otherGestureRecognizer is UIScrollViewPanGestureRecognizer }
步骤3:处理Pan手势,同步TableView滚动
在手势处理方法中,模拟原生滚动的完整流程:
@objc func handlePanGesture(_ sender: UIPanGestureRecognizer) { guard let tableView = self.tableView else { return } let translation = sender.translation(in: tableView) switch sender.state { case .began: // 通知TableView开始滚动状态 tableView.panGestureRecognizer.state = .began case .changed: // 计算新的contentOffset,注意方向(手指向下滑,TableView向上滚,所以y要减去translation.y) var newOffset = tableView.contentOffset newOffset.y -= translation.y // 限制滚动范围,避免超出内容边界(和原生滚动逻辑一致) let minOffsetY = -tableView.contentInset.top let maxOffsetY = tableView.contentSize.height - tableView.bounds.height + tableView.contentInset.bottom newOffset.y = max(minOffsetY, min(newOffset.y, maxOffsetY)) tableView.contentOffset = newOffset // 重置手势的translation,避免下次计算时累加偏移 sender.setTranslation(.zero, in: tableView) case .ended, .cancelled: // 传递手势速度,让TableView处理惯性滚动 let velocity = sender.velocity(in: tableView) tableView.panGestureRecognizer.velocity = CGPoint(x: 0, y: -velocity.y) // 通知TableView结束滚动状态 tableView.panGestureRecognizer.state = .ended default: break } }
方案二:仅传递空白区域的滑动事件(简单版)
如果你的需求是:点击SegmentedControl的选项正常响应,只有在空白区域滑动时才让TableView滚动,可以自定义一个SegmentedControl子类,让空白区域的触摸事件透传给TableView:
class PassThroughSegmentedControl: UISegmentedControl { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let hitView = super.hitTest(point, with: event) // 如果点击的是SegmentedControl本身(空白区域),返回nil让父视图处理事件 if hitView == self { return nil } // 如果点击的是具体的选项,正常响应 return hitView } }
然后把你的SegmentedControl替换成这个子类即可,不需要额外添加Pan手势。
测试的时候记得验证两点:一是SegmentedControl的点击功能是否正常,二是滑动时的惯性、边界回弹是否和原生TableView一致。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

