我有一个UISlider
作为视图的一部分,加载到启用了分页的UIScrollView
中。我注意到了一个意想不到的行为。如果用户试图快速使用滑块(即按下并移动),它“激活”滚动视图,导致页面切换。但是,如果按住一秒钟滑块“激活”,则可以调整滑块值。这种行为是不可取的。
加载到UISlider
时,UIScrollView
响应的最佳方式是什么?我想过添加一个“阻止”视图,它只会占用滑块下面的触摸事件,但不确定这是否是最好的方法。
答案 0 :(得分:44)
UIScrollView
方面无需进行点击测试。因为延迟是由UIScrollView
本身设定的。子类化和实现自定义hitTest:withEvent:
将无济于事,因为它仍然会被延迟触发。
我搜索了几个小时的优雅解决方案,因为我想在ios应用程序切换器中模拟苹果自己的volumelider。
诀窍:
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];
}
}
答案 1 :(得分:3)
让滚动视图检测滑块所占区域的触摸(覆盖hitTest:withEvent:
,您可能需要子类UIScrollView
)。如果检测到触摸所述区域,请告诉滚动视图立即将触摸传递给滑块。
答案 2 :(得分:2)
在创建音频播放器时,我遇到了sliderView
的确切问题,并将其添加到scrollView
,每当我触及sliderView
时,都会使用scrollView
触摸而不是sliderView
,scrollView
移动了。为避免这种情况,我在用户触摸scrollView
时禁用了sliderView
的srcolling,否则启用了滚动功能。
- (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 ;
}
答案 3 :(得分:2)
我的问题是这个问题的超集 - 我在UITableViewCells中有UISliders,整个UITableView是UIScrollView中的一个页面。滑块对与其他两个的交互造成严重破坏,并且子类化解决方案不起作用。以下是我提出的最佳工作方式:当滑块移动时发送通知,并在此期间让UITableView和UIScrollView disableScrolling。请注意下图:我的滑块是水平的,我的桌面视图是垂直的,我的UIScrollView是水平页面。

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;
}
我很想听到一个更优雅的解决方案,但这个肯定能完成工作。使用滑块时,表格和滚动视图保持不变,当您在滑块外部单击时,tableview和scrollviews将按预期移动。另外 - 注意我可以在此解决方案中使用所有3个组件的非子类化实例。希望这有助于某人!
答案 4 :(得分: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)
}
}
}
答案 5 :(得分: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;
}
还为scrollview设置yourScrollView.delaysContentTouches = NO;
属性。