前言
最近在做项目时候有如下这样一个界面
这个页面涉及到视频播放拖动进度条的需求,测试那边提过来的bug是进度条滑块不够灵敏,交互的时候很难响应用户的操作.苦逼码农一枚,提了bug就得改啊.
正文
在网上看了很多关于这方面的处理,总结了下大致3种方法
- 一种是直接改变滑块图片的大小.但是在项目中有时为了整体风格的统一和样式匹配.不方便修改图片大小.所以个人不是很喜欢这个解决方法.
- 第二种,是继承UISlider重写如下方法,细微的扩展一下滑块的响应范围
背景:由于UI给的thumbImage图片过小,默认UISlider开始拖动的手势范围只有thumbImage的大小之内.为了解决这个问题需要创建一个子类继承于UISlider.重写其中的方法:
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
//y轴方向改变手势范围
rect.origin.y = rect.origin.y - 10;
rect.size.height = rect.size.height + 20;
return CGRectInset ([super thumbRectForBounds:bounds trackRect:rect value:value], 10 ,10);
}
将会增加Y轴方向thumbImage的触控范围
这个修改的偏移数值,我还没有做深入研究,好像改太大了的话,滑块会消失.我打印了下bounds和rect好像相差的就是10.所以估计这里的偏移数值不是随便给的,最好不要随便改.
这个方法,我试了一下,效果有,但是不明显,需求不高的情况下可以就用这个方法
*第三种方法,我认为是效果最好的,需要继承UISlider重写如下方法
#define SLIDER_X_BOUND 30
#define SLIDER_Y_BOUND 40
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value;
{
rect.origin.x = rect.origin.x;
rect.size.width = rect.size.width;
CGRect result = [super thumbRectForBounds:bounds trackRect:rect value:value];
//记录下最终的frame
lastBounds = result;
return result;
}
//检查点击事件点击范围是否能够交给self处理
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//调用父类方法,找到能够处理event的view
UIView* result = [super hitTest:point withEvent:event];
if (result != self) {
/*如果这个view不是self,我们给slider扩充一下响应范围,
这里的扩充范围数据就可以自己设置了
*/
if ((point.y >= -15) &&
(point.y < (lastBounds.size.height + SLIDER_Y_BOUND)) &&
(point.x >= 0 && point.x < CGRectGetWidth(self.bounds))) {
//如果在扩充的范围类,就将event的处理权交给self
result = self;
}
}
//否则,返回能够处理的view
return result;
}
//检查是点击事件的点是否在slider范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
//调用父类判断
BOOL result = [super pointInside:point withEvent:event];
if (!result) {
//同理,如果不在slider范围类,扩充响应范围
if ((point.x >= (lastBounds.origin.x - SLIDER_X_BOUND)) && (point.x <= (lastBounds.origin.x + lastBounds.size.width + SLIDER_X_BOUND))
&& (point.y >= -SLIDER_Y_BOUND) && (point.y < (lastBounds.size.height + SLIDER_Y_BOUND))) {
//在扩充范围内,返回yes
result = YES;
}
}
//NSLog(@"UISlider(%d).pointInside: (%f, %f) result=%d", self, point.x, point.y, result);
//否则返回父类的结果
return result;
}
对以上重写方法有疑问的,或则不是很了解的,请移驾该篇文章事件处理,响应者链条
通过上文的方法,基本就可以解决滑块不灵敏的问题了而且效果不错.当然重写方法不一定非要这么写不可,可以根据项目需求和每个人的理解以及技术水准不一样.标准不是唯一的.
看似问题解决了,我终于可以告诉测试我的bug解决了.然而天不遂人愿.在测试demo上一切no problem.但当我运用到项目中问题出现了.滑块给人的感觉还是不是很灵敏.不能直接用手指触碰到滑块立马滑动,必须要按住一会才能很好的滑动.
这个问题困扰好久,最后感谢这篇文章关于ScrollerView的一些小心得
这个界面顶部有一行tab导航条,所以这个界面是需要支持左右滑动切换tab导航条的.这个功能当然是基于scrollView做的,追根究底.还是事件响应处理的问题
UIScrollerView中添加了一个UISlider的组件,在手势滑动的过程中,很难滑动到UISlider这个控件,经常是滑动的时候UIScrollerView进行了滚动,
而UISlider这个控件没有滑动,让人很抓狂。
下面引用一下前辈的总结,因为自己觉得没有他总结的详细
UIScrollView重载了hitTest方法,当手指touch的时候,UIScrollView会拦截所有event,然后等待150ms,在这段时间内,如果没有手指没有移动,当时间结束时,UIScrollView会发送tracking event到子视图上,并且自身不滑动。在时间结束前,手指发生了移动,那么UIScrollView就会进行滑动,从而取消发送tracking。
总结以上所述,总算略有眉目.于是我为这个界面写了一个UIScrollView的类扩展重写了方法:
看来是UIScrollView的问题。直接拖动UISlider,此时touch时间在150ms以内,UIScrollView会认为是拖动自己,从而拦截了event,导致UISlider接受不到滑动的event。但是只要按住UISlider一会再拖动,此时此时touch时间超过150ms,因此滑动的event会发送到UISlider上。
期间试过几种方法,只有一种可行,就是重写UIScrollView的hitTest方法:当滑动UISlider时,使UIScrollView不可滑动。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
/*
直接拖动UISlider,此时touch时间在150ms以内,UIScrollView会认为是拖动自己,从而拦截了event,导致UISlider接受不到滑动的event。但是只要按住UISlider一会再拖动,此时此时touch时间超过150ms,因此滑动的event会发送到UISlider上。
*/
UIView *view = [super hitTest:point withEvent:event];
if([view isKindOfClass:[UISlider class]])
{
//如果响应view是UISlider,则scrollview禁止滑动
self.scrollEnabled = NO;
}
else
{ //如果不是,则恢复滑动
self.scrollEnabled = YES;
}
return view;
}
OK,cmd + R运行项目,大功告成!!!
写在最后
参考:
iOS UISlider滑动块触摸范围调整变大
关于UISlider的拖动手势不灵敏的解决方法
UIScorllView心得