前言
最近在做項(xiàng)目時(shí)候有如下這樣一個(gè)界面
這個(gè)頁(yè)面涉及到視頻播放拖動(dòng)進(jìn)度條的需求,測(cè)試那邊提過來(lái)的bug是進(jìn)度條滑塊不夠靈敏,交互的時(shí)候很難響應(yīng)用戶的操作.苦逼碼農(nóng)一枚,提了bug就得改啊.
正文
在網(wǎng)上看了很多關(guān)于這方面的處理,總結(jié)了下大致3種方法
- 一種是直接改變滑塊圖片的大小.但是在項(xiàng)目中有時(shí)為了整體風(fēng)格的統(tǒng)一和樣式匹配.不方便修改圖片大小.所以個(gè)人不是很喜歡這個(gè)解決方法.
- 第二種,是繼承UISlider重寫如下方法,細(xì)微的擴(kuò)展一下滑塊的響應(yīng)范圍
背景:由于UI給的thumbImage圖片過小,默認(rèn)UISlider開始拖動(dòng)的手勢(shì)范圍只有thumbImage的大小之內(nèi).為了解決這個(gè)問題需要?jiǎng)?chuàng)建一個(gè)子類繼承于UISlider.重寫其中的方法:
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
//y軸方向改變手勢(shì)范圍
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);
}
將會(huì)增加Y軸方向thumbImage的觸控范圍
這個(gè)修改的偏移數(shù)值,我還沒有做深入研究,好像改太大了的話,滑塊會(huì)消失.我打印了下bounds和rect好像相差的就是10.所以估計(jì)這里的偏移數(shù)值不是隨便給的,最好不要隨便改.
這個(gè)方法,我試了一下,效果有,但是不明顯,需求不高的情況下可以就用這個(gè)方法
*第三種方法,我認(rèn)為是效果最好的,需要繼承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;
}
//檢查點(diǎn)擊事件點(diǎn)擊范圍是否能夠交給self處理
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//調(diào)用父類方法,找到能夠處理event的view
UIView* result = [super hitTest:point withEvent:event];
if (result != self) {
/*如果這個(gè)view不是self,我們給slider擴(kuò)充一下響應(yīng)范圍,
這里的擴(kuò)充范圍數(shù)據(jù)就可以自己設(shè)置了
*/
if ((point.y >= -15) &&
(point.y < (lastBounds.size.height + SLIDER_Y_BOUND)) &&
(point.x >= 0 && point.x < CGRectGetWidth(self.bounds))) {
//如果在擴(kuò)充的范圍類,就將event的處理權(quán)交給self
result = self;
}
}
//否則,返回能夠處理的view
return result;
}
//檢查是點(diǎn)擊事件的點(diǎn)是否在slider范圍內(nèi)
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
//調(diào)用父類判斷
BOOL result = [super pointInside:point withEvent:event];
if (!result) {
//同理,如果不在slider范圍類,擴(kuò)充響應(yīng)范圍
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))) {
//在擴(kuò)充范圍內(nèi),返回yes
result = YES;
}
}
//NSLog(@"UISlider(%d).pointInside: (%f, %f) result=%d", self, point.x, point.y, result);
//否則返回父類的結(jié)果
return result;
}
對(duì)以上重寫方法有疑問的,或則不是很了解的,請(qǐng)移駕該篇文章事件處理,響應(yīng)者鏈條
通過上文的方法,基本就可以解決滑塊不靈敏的問題了而且效果不錯(cuò).當(dāng)然重寫方法不一定非要這么寫不可,可以根據(jù)項(xiàng)目需求和每個(gè)人的理解以及技術(shù)水準(zhǔn)不一樣.標(biāo)準(zhǔn)不是唯一的.
看似問題解決了,我終于可以告訴測(cè)試我的bug解決了.然而天不遂人愿.在測(cè)試demo上一切no problem.但當(dāng)我運(yùn)用到項(xiàng)目中問題出現(xiàn)了.滑塊給人的感覺還是不是很靈敏.不能直接用手指觸碰到滑塊立馬滑動(dòng),必須要按住一會(huì)才能很好的滑動(dòng).
這個(gè)問題困擾好久,最后感謝這篇文章關(guān)于ScrollerView的一些小心得
這個(gè)界面頂部有一行tab導(dǎo)航條,所以這個(gè)界面是需要支持左右滑動(dòng)切換tab導(dǎo)航條的.這個(gè)功能當(dāng)然是基于scrollView做的,追根究底.還是事件響應(yīng)處理的問題
UIScrollerView中添加了一個(gè)UISlider的組件凹炸,在手勢(shì)滑動(dòng)的過程中卫病,很難滑動(dòng)到UISlider這個(gè)控件,經(jīng)常是滑動(dòng)的時(shí)候UIScrollerView進(jìn)行了滾動(dòng)特咆,
而UISlider這個(gè)控件沒有滑動(dòng),讓人很抓狂。
下面引用一下前輩的總結(jié)笑诅,因?yàn)樽约河X得沒有他總結(jié)的詳細(xì)
UIScrollView重載了hitTest方法吐辙,當(dāng)手指touch的時(shí)候宣决,UIScrollView會(huì)攔截所有event,然后等待150ms,在這段時(shí)間內(nèi)昏苏,如果沒有手指沒有移動(dòng)尊沸,當(dāng)時(shí)間結(jié)束時(shí)威沫,UIScrollView會(huì)發(fā)送tracking event到子視圖上,并且自身不滑動(dòng)洼专。在時(shí)間結(jié)束前棒掠,手指發(fā)生了移動(dòng),那么UIScrollView就會(huì)進(jìn)行滑動(dòng)屁商,從而取消發(fā)送tracking烟很。
總結(jié)以上所述,總算略有眉目.于是我為這個(gè)界面寫了一個(gè)UIScrollView的類擴(kuò)展重寫了方法:
看來(lái)是UIScrollView的問題。直接拖動(dòng)UISlider蜡镶,此時(shí)touch時(shí)間在150ms以內(nèi)雾袱,UIScrollView會(huì)認(rèn)為是拖動(dòng)自己,從而攔截了event官还,導(dǎo)致UISlider接受不到滑動(dòng)的event芹橡。但是只要按住UISlider一會(huì)再拖動(dòng),此時(shí)此時(shí)touch時(shí)間超過150ms望伦,因此滑動(dòng)的event會(huì)發(fā)送到UISlider上林说。
期間試過幾種方法,只有一種可行屯伞,就是重寫UIScrollView的hitTest方法:當(dāng)滑動(dòng)UISlider時(shí)腿箩,使UIScrollView不可滑動(dòng)。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
/*
直接拖動(dòng)UISlider劣摇,此時(shí)touch時(shí)間在150ms以內(nèi)度秘,UIScrollView會(huì)認(rèn)為是拖動(dòng)自己,從而攔截了event饵撑,導(dǎo)致UISlider接受不到滑動(dòng)的event剑梳。但是只要按住UISlider一會(huì)再拖動(dòng),此時(shí)此時(shí)touch時(shí)間超過150ms滑潘,因此滑動(dòng)的event會(huì)發(fā)送到UISlider上垢乙。
*/
UIView *view = [super hitTest:point withEvent:event];
if([view isKindOfClass:[UISlider class]])
{
//如果響應(yīng)view是UISlider,則scrollview禁止滑動(dòng)
self.scrollEnabled = NO;
}
else
{ //如果不是,則恢復(fù)滑動(dòng)
self.scrollEnabled = YES;
}
return view;
}
OK,cmd + R運(yùn)行項(xiàng)目,大功告成!!!
寫在最后
參考:
iOS UISlider滑動(dòng)塊觸摸范圍調(diào)整變大
關(guān)于UISlider的拖動(dòng)手勢(shì)不靈敏的解決方法
UIScorllView心得