17

我有一个UISlider作为视图的一部分加载到UIScrollView启用分页的视图中。我注意到了一个意外的行为。如果用户试图快速使用滑块(即按下并移动),它会“激活”滚动视图,从而导致页面切换。但是,如果您按住一秒钟滑块“激活”,然后您可以调整滑块值。这种行为是不可取的。

UISlider加载到 中时做出响应的最佳方法是UIScrollView什么?我考虑过添加一个“拦截器”视图,它只会占用放置在滑块下方的触摸事件,但不确定这是否是最好的方法。

4

7 回答 7

47

侧面不需要命中测试UIScrollView。因为延迟是由UIScrollView自己设置的。子类化和实现自定义hitTest:withEvent:将无济于事,因为它仍然会延迟触发。

我搜索了几个小时来寻找一个优雅的解决方案,因为我想在 ios 应用程序切换器中模拟苹果自己的volumeslider。

诀窍:

yourScrollView.delaysContentTouches = NO;

不幸的是,这会禁用UISliders轨道上的事件,因此对于这一部分,您UIScrollView不会触发任何触摸事件,因为它们首先被滑块捕获。

要传递拇指矩形以外的触摸事件,UISliders您必须子类UISlider化并添加以下内容:

// get the location of the thumb
- (CGRect)thumbRect 
{
   CGRect trackRect = [self trackRectForBounds:self.bounds];
   CGRect thumbRect = [self thumbRectForBounds:self.bounds
                                  trackRect:trackRect
                                      value:self.value];
   return thumbRect;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{   
   CGRect thumbFrame = [self thumbRect];

      // check if the point is within the thumb
   if (CGRectContainsPoint(thumbFrame, point))
   {
      // if so trigger the method of the super class
      NSLog(@"inside thumb");
      return [super hitTest:point withEvent:event];
   }
   else
   {
      // if not just pass the event on to your superview
      NSLog(@"outside thumb");
      return [[self superview] hitTest:point withEvent:event];
   }
}
于 2011-06-22T20:03:29.243 回答
3

让您的滚动视图检测对滑块所占据区域的触摸(覆盖hitTest:withEvent:,您可能需要子类化UIScrollView)。如果检测到对所述区域的触摸,请告诉您的滚动视图立即将触摸传递给滑块。

于 2011-01-05T02:24:57.580 回答
2

在创建音频播放器时,我遇到了 a 的确切问题,sliderView它被添加到 ascrollView中,每当我触摸 时sliderViewscrollView用于获取触摸而不是 ,而不是sliderView移动scrollView。为避免这种情况,我禁用了scrollView用户触摸时的 srcolling,sliderView否则启用滚动。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    UIView *result = [super hitTest:point withEvent:event] ;
    if (result == sliderView)
    {
        scrollView.scrollEnabled = NO ;
    }
    else
    {
        scrollView.scrollEnabled = YES ;
    }

    return result ;

}
于 2013-11-19T06:52:47.850 回答
2

我的问题是这个问题的超集——我在 UITableViewCells 中有 UISliders,整个 UITableView 是 UIScrollView 中的一个页面。滑块对与其他两个的交互造成严重破坏,子类化解决方案不起作用。这是我想出的效果很好的方法:在滑块移动时发送通知,并在此期间启用 UITableView 和 UIScrollView disableScrolling。注意下图中:我的滑块是水平的,我的 tableview 是垂直的,我的 UIScrollView 有水平页面。

<img src="https://i.stack.imgur.com/EYykB.png" alt="UIScrollView 中的 UITableView 中的 UISlider">

UITableViewCell 为以编程方式创建的滑块获取事件:

self.numberSlider = [[UISlider alloc] init];
[self.numberSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[self.numberSlider addTarget:self action:@selector(sliderTouchDown:) forControlEvents:UIControlEventTouchDown];
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside];
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpOutside];

出于本教程的目的,我们只关心 touchDown 和 Up:

- (void)sliderTouchDown:(UISlider *)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
}

- (void)sliderTouchUp:(UISlider *)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

现在,我们在 UITableView 中都捕获了这些通知(请注意,tableview 在 VC 中,但我相信如果您进行子类化,这将起作用):

- (void)viewDidLoad
{
    // other stuff
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchDown:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchUp:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

- (void)sliderTouchDown:(NSNotification *)notify
{
    self.treatmentTableView.scrollEnabled = NO;
}

- (void)sliderTouchUp:(NSNotification *)notify
{
    self.treatmentTableView.scrollEnabled = YES;
}

和 UIScrollView (与上面相同,包含在 VC 中):

- (void)viewDidLoad
{
    // other stuff

    // Register for slider notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disableScrolling:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScrolling:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil];
}

- (void)disableScrolling:(NSNotification *)notify
{
    self.scrollView.scrollEnabled = NO;
}

- (void)enableScrolling:(NSNotification *)notify
{
    self.scrollView.scrollEnabled = YES;
}

我很想听听一个更优雅的解决方案,但这绝对可以完成工作。当您使用滑块时,表格和滚动视图保持静止,当您在滑块外部单击时,表格视图和滚动视图按预期移动。另外 - 请注意,我可以在此解决方案中使用所有 3 个组件的非子类实例。希望这对某人有帮助!

于 2014-01-22T00:43:16.577 回答
2

我想将此作为评论发布,但我的帐户没有代表,所以我不得不发布一个完整的答案。user762391 的答案,包括 hitTest 覆盖工作完美。我只想添加它而不是覆盖hitTest,您可以像这样覆盖pointInside:withEvent:(保留thumbRect方法):

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent * _Nullable)event {
    return CGRectContainsPoint([self thumbRect], point)
}

或在斯威夫特:

var thumbRect: CGRect {
    return thumbRectForBounds(bounds, trackRect: trackRectForBounds(bounds), value: value)
}

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
    return thumbRect.contains(point)
}
于 2015-10-07T17:01:48.913 回答
2

Swift 3 中最受好评的评论代码 :)

import Foundation

class UISliderForScrollView:UISlider {
    var thumbRect:CGRect {
        let trackRect = self.trackRect(forBounds: bounds)
        return thumbRect(forBounds: bounds, trackRect: trackRect, value: value)
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if thumbRect.contains(point) {
            return super.hitTest(point, with: event)
        } else {
            return superview?.hitTest(point, with: event)
        }
    }
}
于 2017-01-18T10:53:00.497 回答
0

您可以继承 UIScrollView 并覆盖该方法- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view,如下所示:

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
    if ([view isKindOfClass:[UISlider class]]) {
        UITouch *touch = [[event allTouches] anyObject];
        CGPoint location = [touch locationInView:view];
        CGRect thumbRect;
        UISlider *mySlide = (UISlider*) view;
        CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds];
        thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value];
        if (CGRectContainsPoint(thumbRect, location))
            return YES;
    }
    return NO;
}

还为滚动视图设置yourScrollView.delaysContentTouches = NO;属性。

于 2013-05-22T12:26:11.167 回答