UIGestureRecognizer는 터치 이벤트 처리를 위해 하위보기를 차단합니다.


82

나는 이것이 어떻게 올바른 방법으로 수행되는지 알아 내려고 노력하고 있습니다. 나는 상황을 묘사하려고 노력했습니다. 여기에 이미지 설명 입력

UITableView하위 뷰로 추가 하고 UIView있습니다. UIView있는 누르고 응답 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];
}

답변:


182

나는 매우 유사한 문제가 있었고이 질문에서 내 해결책 찾았습니다 . 요약하면, 자신을 대리인으로 설정 UIGestureRecognizer한 다음 인식기가 터치를 처리하도록 허용하기 전에 대상보기를 확인하십시오. 관련 위임 방법은 다음과 같습니다.

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

3
나는이 솔루션처럼 같은 가장은 터치 덤비는 포함하지 않는 hitTest:withEvent:pointInside:withEvent:.
DarkDust 2011-07-25

6
예를 들어 return !(touch.view == givenView);주어진 뷰를 제외하고 return !(touch.view.tag == kTagNumReservedForExcludingViews);싶 거나 인식기가 다양한 하위 뷰에서 터치를 처리하는 것을 중지 하려는 경우 테스트 할 수있는 깨끗한 솔루션 입니다.
cate dec

6
나는 - (BOOL)isDescendantOfView:(UIView *)view. 이것은 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)eventUIGestureRecognizer를 서브 클래 싱 할 때도 잘 작동합니다 .
Christoph

1
@Justin, 제스처 인식기가 uiscrollview에 속하고 제스처 인식기를 위임 할 수없는 경우 어떻게됩니까?
Gökhan Barış Aker

109

하위보기에 대한 터치 이벤트 차단이 기본 동작입니다. 이 동작을 변경할 수 있습니다.

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

5
이렇게하면 터치 이벤트가 하위보기로 전송되지만 제스처 인식기로도 전송됩니다. 따라서 이것은 하위보기의 차단을 방지하지만 제스처 인식기는 여전히 하위보기에서 인식됩니다.
조나단.

@홍옥. 그래 나도 너와 같은 생각이야. 제스처 처리기 메서드에서 수행 한 작업은 제스처 위치가 관련된 하위 뷰 내에서 발생하는지 확인하는 것입니다.이 경우 코드의 나머지를 실행할 필요가 없습니다. 또한이 해결 방법을 선택한 한 가지 이유는 UITapGestureRecognizer가 translationInView메서드를 선언하지 않기 때문 입니다. 따라서 위에서 언급 한 UIGestureRecognizerDelegate 메서드를 구현하면 오류와 함께 충돌 만 발생 ... unrecognized selector sent to blah합니다. 확인하려면 다음과 같이 사용하십시오 CGRectContainsPoint(subview.bounds, [recognizer locationInView:subview])..
MkVal

OP의 질문에 따르면이 답변을 주요 답변으로 선택해야합니다.
farzadshbfn

5

자체 테이블 뷰가있는 드롭 다운 하위 뷰를 표시하고있었습니다. 결과적으로 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;
}

while 루프가이를 설명합니다
joslinm

5

@Pin Shih Wang 답변을 구축하십시오 . 탭 제스처 인식기가 포함 된보기의 탭을 제외한 모든 탭은 무시됩니다. 모든 탭은 설정 한대로 정상적으로보기 계층으로 전달됩니다 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)
    }
}

제스처 인식기를 어떻게 비교할 수 UISwipeActionStandardButton있습니까? 나는 시도.some(UISwipeActionStandardButton)
Jalil

4

한 가지 가능성은 제스처 인식기를 하위 클래스로 분류하고 (아직하지 않은 경우) 재정 의하여 -touchesBegan:withEvent:각 터치가 제외 된 하위보기에서 시작되었는지 여부를 결정 -ignoreTouch:forEvent:하고 그 경우 해당 터치를 호출 하도록 재정의 하는 것 입니다.

분명히 제외 된 하위보기 또는 제외 된 하위보기의 배열을 추적하려면 속성도 추가해야합니다.


여기에 코드 더미가 있습니다. github.com/…
johndpope

2

어떤 클래스도 상속하지 않고 할 수 있습니다.

제스처의 콜백 선택기에서 gestureRecognizers를 확인할 수 있습니다.

view.gestureRecognizers에 gestureRecognizer가 포함되어 있지 않으면 무시하십시오.

예를 들면

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

여기에서 view.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
    }
}

다른 답변에서 언급 한 바와 같이이 방법을 사용하는 경우, 당신의 뷰 계층에 확실히 전달 탭 제스처 인식기 탭합니다 singleTapGesture.cancelsTouchesInView = NO;추가 viewDidLoad
닉 발색

1

특정 뷰의 수퍼 뷰에 연결된 모든 제스처 인식기를 차단하도록 설계된 UIGestureRecognizer 하위 클래스를 만들었습니다.

내 WEPopover 프로젝트의 일부입니다. 여기에서 찾을 수 있습니다 .


1

parentView의 모든 인식기에 대한 대리자를 구현하고 인식 자의 동시 트리거를 담당하는 대리자에 gestureRecognizer 메서드를 배치합니다.

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

}

U는 자식을 트리거하지만 부모 인식자가 아닌 경우 실패 메서드를 사용할 수 있습니다.

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


0

나도 팝 오버를하고 있었는데 이렇게 했어요

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

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

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
}

이것이하는 일은 탭을 비활성화하는 것입니다. 탭을 인스턴스 변수로 바꾸고 dealloc에서 해제해야했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.