Xcode 9中阻止UIScrollView内UISlider触发滚动的问题咨询
解决UISlider在UIScrollView中滑动触发滚动的问题
我之前也碰到过完全一样的场景,exclusiveTouch确实帮不上忙——它只能处理同一层级视图的触摸竞争,而这里的核心问题是UIScrollView的手势会优先响应横向滑动。下面给你几个经过验证的可行方案:
方法一:通过滑块触摸事件动态控制滚动
这种方法最简单直接,通过监听滑块的触摸开始/结束事件,临时开启/关闭ScrollView的滚动功能:
Swift 代码示例
class YourViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var slider: UISlider! override func viewDidLoad() { super.viewDidLoad() // 绑定滑块触摸事件 slider.addTarget(self, action: #selector(sliderTouchStarted), for: .touchDown) slider.addTarget(self, action: #selector(sliderTouchEnded), for: [.touchUpInside, .touchUpOutside]) } @objc private func sliderTouchStarted() { // 触摸滑块时禁用ScrollView滚动 scrollView.isScrollEnabled = false } @objc private func sliderTouchEnded() { // 松开滑块后恢复滚动 scrollView.isScrollEnabled = true } }
Objective-C 代码示例
@interface YourViewController () @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; @property (weak, nonatomic) IBOutlet UISlider *slider; @end @implementation YourViewController - (void)viewDidLoad { [super viewDidLoad]; [self.slider addTarget:self action:@selector(sliderTouchStarted) forControlEvents:UIControlEventTouchDown]; [self.slider addTarget:self action:@selector(sliderTouchEnded) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside]; } - (void)sliderTouchStarted { self.scrollView.scrollEnabled = NO; } - (void)sliderTouchEnded { self.scrollView.scrollEnabled = YES; } @end
这个方案的优点是实现简单,缺点是如果用户只是点击滑块(不滑动),也会暂时禁用滚动,但大部分场景下这个体验是可以接受的。
方法二:通过手势代理精准控制滚动
UIScrollView自带的panGestureRecognizer可以通过代理判断是否响应手势。我们可以在代理方法里判断触摸点是否落在ImageView或滑块上,若是则让ScrollView的手势不接收该触摸:
Swift 代码示例
class YourViewController: UIViewController, UIGestureRecognizerDelegate { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var targetImageView: UIImageView! @IBOutlet weak var slider: UISlider! override func viewDidLoad() { super.viewDidLoad() scrollView.panGestureRecognizer.delegate = self // 务必打开ImageView的交互开关,否则触摸事件无法传递 targetImageView.isUserInteractionEnabled = true } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { let touchedView = touch.view // 判断触摸点是否在目标ImageView或滑块上 if touchedView === slider || touchedView?.isDescendant(of: targetImageView) == true { return false // 不让ScrollView的手势响应这个触摸 } return true } }
Objective-C 代码示例
@interface YourViewController () <UIGestureRecognizerDelegate> @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; @property (weak, nonatomic) IBOutlet UIImageView *targetImageView; @property (weak, nonatomic) IBOutlet UISlider *slider; @end @implementation YourViewController - (void)viewDidLoad { [super viewDidLoad]; self.scrollView.panGestureRecognizer.delegate = self; self.targetImageView.userInteractionEnabled = YES; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { UIView *touchedView = touch.view; if (touchedView == self.slider || [touchedView isDescendantOfView:self.targetImageView]) { return NO; } return YES; } @end
这个方案更精准,只要触摸在ImageView或滑块上,ScrollView的滚动手势就完全不响应,完美匹配你的需求。
方法三:自定义ScrollView全局复用
如果项目中有多个类似场景,可以自定义UIScrollView,重写触摸判断方法实现全局控制:
Swift 代码示例
class CustomScrollView: UIScrollView { override func touchesShouldCancel(in view: UIView) -> Bool { // 若视图是滑块或ImageView的子视图,不取消触摸事件(即不让ScrollView接管滚动) if view is UISlider || view.isDescendant(of: UIImageView.self) { return false } return super.touchesShouldCancel(in: view) } }
Objective-C 代码示例
@interface CustomScrollView : UIScrollView @end @implementation CustomScrollView - (BOOL)touchesShouldCancelInContentView:(UIView *)view { if ([view isKindOfClass:[UISlider class]] || [view isDescendantOfView:[UIImageView class]]) { return NO; } return [super touchesShouldCancelInContentView:view]; } @end
之后在Xcode中将你的ScrollView类改为CustomScrollView即可,无需在每个ViewController重复写逻辑。
我个人更推荐方法二,控制粒度细且无需修改ScrollView类,单个ViewController内就能解决问题。记得一定要打开ImageView的userInteractionEnabled,否则触摸事件无法传递到滑块上哦!
内容的提问来源于stack exchange,提问作者tech74
相关产品推荐
相关产品推荐

