UITapGestureRecognizer가있는 뷰 내부의 UIButton


184

와 함께 볼 수 있습니다 UITapGestureRecognizer. 뷰를 탭하면이 뷰 위에 다른 뷰가 나타납니다. 이 새로운 뷰에는 3 개의 버튼이 있습니다. 이제이 단추 중 하나를 누르면 단추 동작이 나타나지 않고 탭 동작 동작 만됩니다. 더 이상이 버튼을 사용할 수 없습니다. 이 버튼을 통해 이벤트를 진행하려면 어떻게해야합니까? 이상한 점은 버튼이 여전히 강조 표시된다는 것입니다.

탭을받은 후에 UITapGestureRecognizer를 제거 할 수 없습니다. 그것으로 새로운보기를 제거 할 수 있기 때문입니다. 전체 화면 보라 컨트롤과 같은 동작을 원한다는 의미 입니다.

답변:


256

컨트롤러 또는보기 (제스쳐 인식기를 생성하는 것)를의 대리자로 설정할 수 있습니다 UITapGestureRecognizer. 그런 다음 대리자에서 구현할 수 있습니다 -gestureRecognizer:shouldReceiveTouch:. 구현에서 터치가 새로운 하위 뷰에 속하는지 테스트하고, 그렇다면 터치 인식기에 지시하도록 지시 할 수 있습니다. 다음과 같은 것 :

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

내 헤더 파일에서 UIGestoreRegognizerDelegate를 구현하는 뷰가 있고 내 .mi에서 위의 코드를 추가했습니다. 탭은이 방법으로 절대 들어 가지 않으며 처리기로 바로갑니다. 어떤 아이디어?
— kmehta

1
@kmehta UIGestureRecognizer 델리게이트 속성을 설정하는 것을 잊어 버렸습니다.
— 까지

IBAction이 관련된 모든 경우를 처리하기 위해이 답변을 일반화 할 수 있습니까?
— Martin Wickman

@Martin IBAction은 컴파일 할 때까지 void런타임에 감지에 사용할 정보를 남기지 않습니다. 그러나 할 수있는 일은 뷰 계층을 걸어 테스트 하고 컨트롤을 찾으면 UIControl반환 NO하는 것입니다.
— 릴리 발라드

이것을 위해 thx, 그것은 나를 도와줍니다. 버튼이 UIImageView에 있으면 imageView의 userInteractionEnabled가 YES로 설정되어 있는지 확인하십시오.
— james075

156

Casey의 후속 조치로 Kevin Ballard의 답변을 따르십시오.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

이것은 기본적으로 버튼, 슬라이더 등과 같은 모든 사용자 입력 유형의 컨트롤을 작동시킵니다.


1
setCancelsTouchesInView = NO컨트롤과의 상호 작용시 탭 제스처를 트리거하지 않는 유연한 솔루션입니다 . 그러나 더 잘 작성할 수 있습니다.return ![touch.view isKindOfClass:[UIControl class]];
— griga13

! 스위프트 4+ 솔루션 : 리턴 (touch.view은 UIControl입니다)
— nteissler

103

이 답변을 여기에서 찾았습니다 : link

당신은 또한 사용할 수 있습니다

tapRecognizer.cancelsTouchesInView = NO;

탭 인식기가 모든 탭을 잡는 유일한 사람이되는 것을 방지합니다.

UPDATE - 마이클 이 속성을 설명하는 문서에 대한 링크를 언급 : cancelsTouchesInView


8
허용되는 답변 IMO 여야합니다. 이렇게하면 모든 하위 뷰가 탭을 자체적으로 처리 할 수 ​​있으며 탭 인식기가 연결된 뷰가 탭을 '하이재킹'하지 못합니다. 원하는 경우 뷰를 클릭 가능하게 만드는 것만으로 델리게이트를 구현할 필요가 없습니다 (첫 번째 응답자를 사임하고 텍스트 필드에서 키보드를 숨기는 등).
— EeKay

4
이것은 분명히 받아 들여질만한 대답이어야합니다. 이 답변을 통해 Apple 설명서를 올바르게 읽었 으므로 제스처 인식기 가 이렇게 하지 않으면 하위보기에서 인식 된 이벤트를 받지 못하게됩니다 .
— 마이클 반 데르 Westhuizen

1
이것은 작동했지만 지금은 작동하지 않습니다. 아마도 버튼 아래에 scrollView가 있기 때문에 아마도? 위의 답변과 같이 대리인을 구현해야했습니다.
— xissburg

7
약간의 실험을 한 후에 제스처 인식기가 포함 된보기가 UIControl의 형제 인 경우에만 작동하며보기가 UIControl의 부모 인 경우에는 작동하지 않습니다.
— xissburg

6
의도 한대로 작동하지는 않습니다 ... 예, 탭 인식기가 UIControl에서 이벤트를 먹지 못하게합니다. 그러나 여전히 인식기 동작이 실행되고 동시에 2 동작이 실행됩니다.
— Tek Yin

71

Kevin Ballard의 답변에 대한 후속 조치로 동일한 문제가 발생 하여이 코드를 사용하게되었습니다.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[UIButton class]]){
        return NO;
    }
    return YES;
}

동일한 효과가 있지만 모든 뷰 깊이에서 모든 UIButton에서 작동합니다 (UIButton은 여러 뷰 깊이였으며 UIGestureRecognizer의 대리자는 참조하지 않았습니다).


6
여기서 거시기가되고 싶지 않지만 실제로는 그렇습니다. 코코아 규칙을 사용하십시오. TRUE / FALSE / NULL은 CoreFoundation-Layer를위한 것입니다.
— steipete

내가 읽은 것에서 YES와 NO는 실제로 가독성을 위해 만들어졌습니다. 가독성은 주관적입니다. 모든 사람이 지나치게 염려하지 않는 방법 및 속성 명명 규칙에서 비롯됩니다. 명명 규칙을 엄격히 준수하려면 NSWhatever에 대해 YES와 NO를 사용하십시오. 그러나 개발자를 설문 조사하면 이들 중 어느 것이 더 읽기 쉬운 지에 대한 의견이 다를 것입니다. [button setHidden : YES]; 또는 [button setHidden : TRUE];
— Pimp Juice McJones

1
내가 말하려고하는 것은 이름 지정 규칙의 전제가 가독성이라면 어떤 경우에는 주관적이라는 것을 부인하기 어렵다는 것입니다. 당신이 순수 주의자라면 그 진술을 이해하지 못할 것입니다.
— Pimp Juice McJones

2
그것은 주관적인 것처럼 보일지 모르지만 코코아 프로그래밍이 끝난 후에는 의미 적으로 정확한 코드 흐름에 실제로 도달합니다. 잘.
— Kendall Helmstetter Gelner 2018 년

UITouch 이벤트 내부로 Touch Up Inside UITouch 이벤트 (UIButton을 탭하여 생성 된 이벤트)로 탭 제스처를 언제 또는 어떻게 전달할 수 있습니까? 탭 제스처와 이미 연결된 15 개의 터치 이벤트가있는 버튼에 대한 탭 제스처 인식기를 만드는 것은 복제적이고 잘못된 것 같습니다 . 추측이 아닌 코드를보고 싶습니다.
— James Bush

10

iOS 6.0 이상에서 기본 제어 조치는 겹치는 제스처 인식기 동작을 방지합니다. 예를 들어, 버튼의 기본 동작은 단일 탭입니다. 버튼의 상위보기에 단일 탭 제스처 인식기가 연결되어 있고 사용자가 버튼을 누르면 버튼의 동작 방법은 제스처 인식기 대신 터치 이벤트를 수신합니다. 이것은 컨트롤의 기본 동작과 겹치는 제스처 인식에만 적용됩니다.

Apple의 API 문서에서


8

이 답변은 불완전했습니다. 이 부울 연산을 사용하는 방법에 대한 여러 게시물을 읽어야했습니다.

* .h 파일에 이것을 추가하십시오

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

* .m 파일에 이것을 추가하십시오

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

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.



    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];


// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}


-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

7

여기에서 다른 방법을 찾았 습니다 . 각 버튼 내부의 터치 여부를 감지합니다.

(1) pointInside : withEvent : (2) locationInView :

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    // Don't recognize taps in the buttons
    return (![self.button1 pointInside:[touch locationInView:self.button1] withEvent:nil] &&
            ![self.button2 pointInside:[touch locationInView:self.button2] withEvent:nil] &&
            ![self.button3 pointInside:[touch locationInView:self.button3] withEvent:nil]);
}

나는 이것을 위해 다른 모든 해결책을 시도했지만 -pointInside : withEvent : 접근 방식이 나에게 유일한 방법이었다.
— Kenny Wyland

3

나를 위해 일한 Lily Ballard의 Swift 버전 답변 은 다음과 같습니다 .

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (scrollView.superview != nil) {
        if ((touch.view?.isDescendantOfView(scrollView)) != nil) { return false }
    }
    return true
}

3

스위프트 5

tapgesture가있는 수퍼 뷰의 버튼

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let _ = touch.view as? UIButton { return false }
    return true
}

필자의 경우 hitTest를 구현합니다. 이 저에게 이었습니다. 버튼으로 컬렉션보기를했습니다.

이 메소드 point(inside:with:)는 각 서브 뷰 의 메소드를 호출하여 터치 이벤트를 수신해야하는 서브 뷰를 판별 하여 뷰 계층 구조를 순회합니다 . 경우 point(inside:with:)true를 반환 지정된 포인트를 포함한 맨 앞보기가 발견 될 때까지, 다음 서브 뷰의 계층 구조는 유사 이송됩니다.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard isUserInteractionEnabled else { return nil }

    guard !isHidden else { return nil }

    guard alpha >= 0.01 else { return nil }

    guard self.point(inside: point, with: event) else { return nil }

    for eachImageCell in collectionView.visibleCells {
        for eachImageButton in eachImageCell.subviews {
            if let crossButton = eachImageButton as? UIButton {
                if crossButton.point(inside: convert(point, to: crossButton), with: event) {
                    return crossButton
                }
            }
        }
    }
    return super.hitTest(point, with: event)
}

1

다음 부울을 설정하여 UITapGestureRecognizer가 다른 이벤트 (예 : 단추 누르기)를 취소하지 못하게 할 수 있습니다.

    [tapRecognizer setCancelsTouchesInView:NO];

1

시나리오가 다음과 같은 경우 :

간단한보기와 일부 UIButtons, UITextField 컨트롤이 해당보기에 하위보기로 추가되었습니다. 이제 컨트롤 (추가 한 하위보기)을 제외한보기의 다른 곳을 터치하면 키보드가 사라집니다.

그런 다음 해결책은 다음과 같습니다.

XYZViewController.m에 다음 메소드를 추가하십시오 (보기가 있음)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

먼저 @cdasher 의 답변으로 시도했지만 버튼을 클릭해도 ViewController에서 touch.view 를 내 "UIButton"컨트롤이 아닌 "보기"로 얻 습니다 . 누군가 터치의 뷰 속성이 탭이 발생한 원래 뷰를 반환하지 않는 이유를 말할 수 있습니까
— NaveenRaghuveer

1
스크롤 이벤트 또는 터치 이벤트를 주장하는 다른보기가 있으면 작동하지 않습니다.
— lkraider

1

Cdasher의 답변을 최적화하면

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch 
{
    return ![touch.view isKindOfClass:[UIControl class]];
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.