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.