2011-03-07 55 views
72

我想弄清楚如何完成正確的方式。我試圖描繪的情況: enter image description hereUIGestureRecognizer塊子視圖處理觸摸事件

我加入一個UITableView作爲UIView的子視圖。 UIView響應tap和pinchGestureRecognizer,但是當這樣做時,tableview停止對這兩個手勢作出反應(它仍然對滑動作出反應)。

我已經使它與下面的代碼一起工作,但它顯然不是一個很好的解決方案,我相信有更好的方法。這是擺在UIView(SuperView把):

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 
    if([super hitTest:point withEvent:event] == self) { 
     for (id gesture in self.gestureRecognizers) { 
      [gesture setEnabled:YES]; 
     } 
     return self; 
    } 
    for (id gesture in self.gestureRecognizers) { 
     [gesture setEnabled:NO]; 
    } 
    return [self.subviews lastObject]; 
} 

回答

175

我有一個非常類似的問題,發現我的解決方案in this SO question。總之,將自己設置爲您的UIGestureRecognizer的代表,然後在允許識別器處理觸摸之前檢查目標視圖。相關的代理方法是:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch 
+3

我喜歡這個解決方案最大,因爲它不涉及弄亂觸摸,'hitTest:withEvent:'或'pointInside:withEvent:'。 – DarkDust 2011-07-25 10:26:20

+6

乾淨的解決方案,您可以使用例如'return!(touch.view == givenView);'如果你只是想排除一個給定的視圖或'返回!(touch.view.tag == kTagNumReservedForExcludingViews);'當你想停止你的識別器處理觸摸整個一堆不同的子視圖。 – cate 2012-12-29 22:40:48

+6

我會用' - (BOOL)isDescendantOfView:(UIView *)view'做命中測試。這在' - (void)touchesBegan:(NSSet *)與UIEvent:(UIEvent *)事件'在UISestureRecognizer的子類中觸及時也可以正常工作。 – Christoph 2013-03-13 22:27:13

4

一種可能性是繼承你的手勢識別(如果你還沒有的話),並覆蓋-touchesBegan:withEvent:使得它確定每個觸摸是否開始在排除的子視圖和如果有的話,請撥打-ignoreTouch:forEvent:

很明顯,您還需要添加一個屬性以跟蹤排除的子視圖,或者更好的方式是排除一些子視圖。

+0

有代碼堆在這裏https://github.com/search?l=objective-c&q=uigesturerecognizer&ref=cmdform&type=Code – johndpope 2013-05-15 00:11:58

92

將觸摸事件阻塞到子視圖是默認行爲。你可以改變這種行爲:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)]; 
r.cancelsTouchesInView = NO; 
[agentPicker addGestureRecognizer:r]; 
+4

這將觸發事件發送到子視圖,但它也將被髮送到手勢識別器。所以這將防止子視圖的阻塞,但手勢識別器仍然會在子視圖上被識別。 – 2012-08-31 09:38:36

+0

@Jonathan。是的,我同意你的意見。我在我的手勢處理器方法中做的是檢查手勢位置是否發生在相關的子視圖內,在這種情況下,我不需要執行代碼的其餘部分。另外,我選擇這個解決方法的原因之一是UITapGestureRecognizer沒有聲明'translationInView'方法。因此,實現上面提到的UIGestureRecognizerDelegate方法只會導致崩潰錯誤'...無法識別的選擇器發送到等等。要檢查,請使用如下內容:'CGRectContainsPoint(subview.bounds,[識別器locationInView:子視圖])'。 – MkVal 2014-01-30 09:00:03

+0

根據OP的問題,應該選擇這個答案作爲主要答案。 – farzadshbfn 2017-12-05 13:07:42

0

你可以將其關閉並....在我的代碼我做了這樣的事情,我需要把它關掉,當鍵盤不顯示,就可以用它您的情況:

通話,這是viewDidLoad中等等:

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil]; 
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil]; 

然後創建兩個方法:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=true; // turn the gesture on 
} 

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{ 
    tap.enabled=false; //turn the gesture off so it wont consume the touch event 
} 

這樣做會禁用水龍頭。我不得不將tap變成一個實例變量,然後在dealloc中釋放它。

4

我正在顯示一個有自己的tableview的下拉式子視圖。因此,touch.view有時會返回像UITableViewCell類。我必須通過超類來確保它是我認爲它的子類:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
{ 
    UIView *view = touch.view; 
    while (view.class != UIView.class) { 
     // Check if superclass is of type dropdown 
     if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own 
      NSLog(@"Is of type dropdown; returning NO"); 
      return NO; 
     } else { 
      view = view.superview; 
     } 
    } 

    return YES; 
} 
+0

while循環佔了那個 – joslinm 2016-08-17 20:02:09

2

有可能沒有繼承任何類。

您可以檢查gestureRecognizers手勢的回調選擇

如果view.gestureRecognizers不包含您gestureRecognizer,只是忽略它

例如

- (void)viewDidLoad 
{ 
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(handleSingleTap:)]; 
    singleTapGesture.numberOfTapsRequired = 1; 
} 

檢查視圖。gestureRecognizers這裏

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer 
{ 
    UIEvent *event = [[UIEvent alloc] init]; 
    CGPoint location = [gestureRecognizer locationInView:self.view]; 

    //check actually view you hit via hitTest 
    UIView *view = [self.view hitTest:location withEvent:event]; 

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) { 
     //your UIView 
     //do something 
    } 
    else { 
     //your UITableView or some thing else... 
     //ignore 
    } 
} 
+0

正如在其他答案中提到的,如果使用這種方法確保點擊手勢識別器將點擊轉發到您的視圖層次: 'singleTapGesture.cancelsTouchesInView = NO;'添加到上面的'viewDidLoad' – 2017-09-27 12:14:16

1

我創建了一個子類UIGestureRecognizer設計用於阻止附接至一個特定的視圖的所有superviews手勢識別。

這是我的WEPopover項目的一部分。你可以找到它here

0

我也做酥料餅的,這是我做的

func didTap(sender: UITapGestureRecognizer) { 

    let tapLocation = sender.locationInView(tableView) 

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) { 
     sender.cancelsTouchesInView = false 
    } 
    else { 
     delegate?.menuDimissed() 
    } 
} 
0

實現委託的parentView的所有識別器,並把gestureRecognizer方法,它負責識別的同時觸發委託:

func gestureRecognizer(UIGestureRecognizer,  shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool { 
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) { 
    return true 
    } else { 
    return false 
} 

}

U可以使用故障方法如果u想使孩子們被觸發,但不是父識別:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

1

大廈@Pin施王answer。我們忽略除包含輕擊手勢識別器的視圖以外的所有輕敲。正如我們設置tapGestureRecognizer.cancelsTouchesInView = false一樣,所有輕敲都會正常轉發到視圖層次結構。這裏是Swift3/4的代碼:

func ensureBackgroundTapDismissesKeyboard() { 
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) 
    tapGestureRecognizer.cancelsTouchesInView = false 
    self.view.addGestureRecognizer(tapGestureRecognizer) 
} 

@objc func handleTap(recognizer: UIGestureRecognizer) { 
    let location = recognizer.location(in: self.view) 
    let hitTestView = self.view.hitTest(location, with: UIEvent()) 
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) { 
     // I dismiss the keyboard on a tap on the scroll view 
     // REPLACE with own logic 
     self.view.endEditing(true) 
    } 
}