2012-07-14 91 views
20

我希望我的UIScrollView及其子視圖都能接收子視圖內的所有觸摸事件。每個人都可以用自己的方式迴應。允許UIScrollView及其子視圖同時響應觸摸

或者,如果輕拍手勢被轉發到子視圖,一切都會好的。

很多人都在這個普遍的領域掙扎。這裏有幾個的許多相關問題:

How does UIScrollView steal touches from its subviews
How to steal touches from UIScrollView?
How to Cancel Scrolling in UIScrollView

順便說一句,如果我重寫則hitTest:withEvent:方法在滾動視圖,我看到了觸摸只要userInteractionEnabled是YES 。但是這並不能真正解決我的問題,因爲:

1)在這一點上,我不知道它是否是輕拍。
2)有時我需要將userInteractionEnabled設置爲NO。

編輯:爲了澄清,是的,我想對待水龍頭不同於平底鍋。點擊應該通過子視圖來處理。平底鍋可以通過常規方式通過滾動視圖進行處理。

+4

設置userInteractionEnabled NO和預期用戶交互的數據似乎是一個宇宙打破悖論。 – borrrden 2012-07-14 16:11:10

+0

你想要在觸摸和滾動之間尋求解脫嗎? – shannoga 2012-07-14 16:29:42

回答

38

首先,一個聲明延長。如果在UIScrollView上將userInteractionEnabled設置爲NO,則不會將觸摸事件傳遞給子視圖。據我所知,除了一個例外,沒有辦法解決這個問題:截取UIScrollView的超級視圖上的觸摸事件,並且特別將這些事件傳遞給UIScrollView的子視圖。說實話,我不知道你爲什麼想這樣做。如果你想禁用特定的UIScrollView功能(比如......好,滾動),你可以很容易地做到這一點,而不會禁用UserInteraction。

如果我理解你的問題,你需要點擊事件來處理UIScrollView 傳遞給子視圖?在任何情況下(不管是什麼手勢),我認爲你要找的是協議gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:協議UIGestureRecognizerDelegate。在你的子視圖中,無論你使用哪種手勢識別器,都可以在手勢識別器上設置一個代表(可能是任何課程首先設置的UIGestureReconginzer)。覆蓋上述方法並返回YES。現在,此手勢將與可能「偷走」手勢的任何其他識別器一起識別(在您的情況下爲敲擊)。使用這種方法,您甚至可以微調代碼,僅將特定類型的手勢發送到子視圖,或僅在某些情況下發送手勢。它給你很多控制。只是一定要閱讀有關方法,尤其是這部分:

時調用此方法時 要麼gestureRecognizer或otherGestureRecognizer識別手勢會阻止從認識到它的手勢 其他手勢識別。請注意,返回YES的 保證允許同時識別;另一方面,返回「否」的 不能保證防止 同時識別,因爲其他手勢識別器的 委託人可能會返回YES。

當然,有一個警告:這隻適用於手勢識別器。因此,如果您嘗試使用touchesBegan:,touchesEnded等來處理觸摸,則可能仍然存在問題。當然,您可以使用hitTest:將原始觸摸事件發送到子視圖,但爲什麼?爲什麼在UIView中使用這些方法處理事件時,您可以將UIGestureRecognizer附加到視圖並免費獲得所有這些功能?如果你需要觸摸處理方式,沒有標準UIGestureRecognizer可以提供,子類UIGestureRecognizer並在那裏處理觸摸。這樣,您就可以獲得UIGestureRecognizer的所有功能以及您自己的自定義觸摸處理功能。我真的認爲蘋果打算用UIGestureRecognizer取代開發人員在UIView上使用的大多數(如果不是全部的話)觸摸處理代碼。它允許代碼重用,並且在緩解什麼代碼處理什麼觸摸事件時處理起來要容易得多。

+1

太棒了,謝謝!如果其他人正在閱讀此內容,則可以使用UIScrollView方法獲得類似的效果touchesShouldCancelInContentView – 2012-07-20 05:09:14

+0

「如果您想禁用特定的UIScrollView功能(如......好,滾動),您可以輕鬆完成而不禁用UserInteraction「 - >」myScrollView.scrollEnabled = false「 – RGML 2013-01-31 17:03:05

0

如果您需要的是觸摸和滾動之間的差異,那麼您可以測試觸摸是否已移動。如果這是一個水龍頭,那麼touchHasBeenMoved將不會被調用,那麼你可以認爲這是一個觸摸。

此時,您可以設置一個布爾值來指示是否存在一個movnent,並將此布爾值設置爲其他方法中的條件。

我在路上,但如果這是你所需要的,我將能夠在以後更好地解釋。

3

我不知道這是否可以幫助您,但我遇到了類似的問題,我希望scrollview能夠處理雙擊,但只需將單擊轉發給子視圖。這是在CustomScrollView

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 

    UITouch* touch = [touches anyObject]; 
    // Coordinates 
    CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]]; 

    // One tap, forward 
    if(touch.tapCount == 1){ 
     // for each subview 
     for(UIView* overlayView in self.subviews){ 
      // Forward to my subclasss only 
      if([overlayView isKindOfClass:[OverlayView class]]){ 
       // translate coordinate 
       CGPoint newPoint = [touch locationInView:overlayView]; 
       //NSLog(@"%@",NSStringFromCGPoint(newPoint)); 

       BOOL isInside = [overlayView pointInside:newPoint withEvent:event]; 
       //if subview is hit 
       if(isInside){ 
        Forwarding 
        [overlayView touchesEnded:touches withEvent:event]; 
        break; 
       } 
      } 
     } 

    } 
    // double tap : handle zoom 
    else if(touch.tapCount == 2){ 

     if(self.zoomScale == self.maximumZoomScale){ 
      [self setZoomScale:[self minimumZoomScale] animated:YES]; 
     } else { 
      CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];    

      [self zoomToRect:zoomRect animated:YES]; 
     } 

     [self setNeedsDisplay]; 

    } 
} 

當然使用的代碼,有效代碼應該改變,但在這一點上,你應該有你需要決定,如果你要轉發該事件的所有信息。您可能需要在touchesMoved:withEvent:另一種方法中實現此功能。

希望這可以幫助。

0

實現你的目標的一種詭異的方式 - 不是100%確切的 - 是爲UIWindow子類化並覆蓋 - (void)sendEvent:(UIEvent *)事件;

一個簡單的例子:在

頭SecondResponderWindow.h

//SecondResponderWindow.h 

@protocol SecondResponderWindowDelegate 
- (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView; 
- (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView; 
@end 

@interface SecondResponderWindow : UIWindow 
@property (nonatomic, retain) UIView *viewToObserve; 
@property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves; 
@end 
在SecondResponderWindow.m

//SecondResponderWindow.m 

- (void)forwardTouchBegan:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchBegan:touch onView:aView]; 
} 
- (void)forwardTouchMoved:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchMoved:touch onView:aView]; 
} 
- (void)forwardTouchEnded:(id)touch onView:(UIView*)aView { 
    [controllerThatObserves userTouchEnded:touch onView:aView]; 
} 

- (void)sendEvent:(UIEvent *)event { 
    [super sendEvent:event]; 

    if (viewToObserve == nil || controllerThatObserves == nil) return; 

    NSSet *touches = [event allTouches]; 
    UITouch *touch = [touches anyObject]; 
    if ([touch.view isDescendantOfView:viewToObserve] == NO) return; 

    CGPoint tapPoint = [touch locationInView:viewToObserve]; 
    NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint]; 

    if (touch.phase == UITouchPhaseBegan) 
     [self forwardTouchBegan:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseMoved) 
     [self forwardTouchMoved:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseEnded) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
    else if (touch.phase == UITouchPhaseCancelled) 
     [self forwardTouchEnded:pointValue onView:touch.view]; 
} 

這不是100%符合你的期待了 - 因爲你的第二個響應view不會通過-touchDidBegin:原生處理觸摸事件,並且必須實現SecondResponderWindowDelegate。然而,這個破解允許你處理其他響應者的觸摸事件。

此方法由啓發和從MITHIN KUMAR的TapDetectingWindow

2

我遇到了同樣的問題,但在UIPageViewController之內的滾動視圖,所以它必須稍微有點不同。

通過將UIScrollView上的每個識別器的cancelsTouchesInView屬性更改爲false,我能夠接收到UIPageViewController中的按鈕的觸摸。

我這樣做是通過將此代碼添加到viewDidLoad

guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else { 
    print("No gesture recognizers on scrollview.") 
    return 
} 

for recognizer in recognizers { 
    recognizer.cancelsTouchesInView = false 
} 
相關問題