最近看到有人問這個問題:在UIScrollView上添加了一個View,View上面有圖片等元素,需要對單擊和滑動事件進行區分。這樣的問題有以下幾個經常會用到的地方:
(1)點擊UIScrollView上的圖片,跳轉到其他頁面;同時不影響UIScrollView的滑動操作。
(2)在閱讀瀏覽類產品會經常用到,點擊屏幕的中間區域,隱藏/顯示狀態欄或導航條;同時不影響UIScrollView的滑動翻頁操作。
有解決方法事在點擊區域添加一個透明的Button,但個人感覺比較複雜,而且效果並不好。以下是我個人的解決方法:
首先了解下UIScrollView對於touch事件的接收處理原理:UIScrollView重載hitTest 方法,並總會返回itself 。所以所有的touch 事件都會進入到它自己裏面去了。內部的touch事件檢測到這個事件是不是和自己相關的,或者處理或者除遞給內部的view。
爲了檢測touch是處理還是傳遞,UIScrollView當touch產生一個timer。
(1)如果150ms內touch未產生移動,它就把這個事件傳遞給內部view;
(2)如果150ms內touch產生移動,開始scrolling,不會傳遞給內部的view。(如當你touch一個table時候,直接scrolling,你touch的那行永遠不會highlight。)
(3)如果150ms內touch未產生移動並且UIScrollView開始傳遞內部的view事件,但是移動足夠遠的話,且canCancelContentTouches = YES,UIScrollView會調用touchesCancelled方法,cancel掉內部view的事件響應,並開始scrolling。(如當你touch一個table, 停止了一會,然後開始scrolling,那一行就首先被highlight,但是隨後就不在高亮了)
先前提到的在UIScrollView內部的view點擊區域添加透明Button的解決方法,就是需要設置canCancelContentTouches = YES,而且還需重寫UIScrollView的touchesShouldCancelInContentView:(UIView *)view方法,在view爲UIButton的時候,返回YES。不然點擊到button上的事件已經被button接收了,無法cancle掉。另外針對閱讀瀏覽頁面,一般手指都會停留在UIScrollView滾動,如果需要隱藏/顯示狀態欄或導航條,則會出現每點擊一次就出現隱藏/顯示問題。
解決方法:該問題主要的難題要判斷單擊事件。由於150ms很小,因此基本上手指一接觸到UIScrollView就會傳遞到內部的view上,此時內部的view需對這個touch進行時間節點的判斷,判斷它是一個單擊事件,然後執行單擊操作。
1、TestView是ScrollView的subview,也可也是繼承UIScrollView
TestView.h
- @interface TestView : UIView
- {
- UIImageView *imageView;
- NSTimeInterval touchTimer; //記錄touch時間,來控制點擊和滑動判斷
- }
- @property(nonatomic, retain) UIImageView *imageView;
- @property(nonatomic, assign) NSTimeInterval touchTimer;
TestView.m
- @implementation TestView
- #pragma mark -
- #pragma mark Touch Method
- @synthesize imageView;
- @synthesize touchTimer;
- //thouchesBegan 獲取到touch的時間點
- - (void)touchesBegan:(NSSet *)touches
- withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- self.touchTimer = [touch timestamp];
- }
- //touchesEnded,touch事件完成,根據此時時間點獲取到touch事件的總時間,
- - (void)touchesEnded:(NSSet *)touches
- withEvent:(UIEvent *)event
- {
- UITouch *touch = [touches anyObject];
- self.touchTimer = [touch timestamp] - self.touchTimer;
- NSUInteger tapCount = [touch tapCount];
- CGPoint touchPoint = [touch locationInView:self];
- //判斷單擊事件,touch時間和touch的區域
- if (tapCount == 1 && self.touchTimer <= 3 && CGRectContainsPoint(self.imageView.frame, touchPoint))
- {
- //進行單擊的跳轉等事件
- }
- }