You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:26:47