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.