UITapGestureRecognizer가 UITableView를 중단 함 didSelectRowAtIndexPath


207

키보드가 나타나면 텍스트 필드를 위로 스크롤하는 자체 기능을 작성했습니다. 텍스트 필드를 두드려 키보드를 닫으려면 텍스트 필드에서 UITapGestureRecognizer첫 번째 응답자를 사임하도록 처리 하는 키보드를 만들었 습니다.

이제 텍스트 필드에 자동 완성 기능을 만들었습니다. UITableView 텍스트 필드 바로 아래 사용자가 텍스트를 입력 할 때 항목으로 채우는 텍스트 필드에 .

그러나 자동 완성 표에서 항목 중 하나를 선택하면 didSelectRowAtIndexPath 호출되지 않습니다. 대신 탭 제스처 인식기가 호출되어 첫 번째 응답자를 사직 한 것으로 보입니다.

탭 제스처 인식기에게 탭 메시지를 아래로 계속 전달하도록 지시 할 수있는 방법이 있다고 생각 UITableView하지만 그것이 무엇인지 알 수는 없습니다. 어떤 도움이라도 대단히 감사하겠습니다.


이 스레드를 참조하십시오 .
던컨 배비지

답변:


258

좋아, 마침내 제스처 인식기 문서를 검색 한 후 발견했습니다.

해결책은 다음을 구현 UIGestureRecognizerDelegate하고 추가하는 것입니다.

////////////////////////////////////////////////////////////
// UIGestureRecognizerDelegate methods

#pragma mark UIGestureRecognizerDelegate methods

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:autocompleteTableView]) {

        // Don't let selections of auto-complete entries fire the 
        // gesture recognizer
        return NO;
    }

    return YES;
}

그것은 그것을 처리했다. 바라건대 이것은 다른 사람들에게도 도움이 될 것입니다.


또한 텍스트 필드의 탭을 건너 뛰어야합니다.
Steven Kramer

5
내가 더 운이return ![touch.view isKindOfClass:[UITableViewCell class]];
조쉬 Paradroid

1
@Josh 셀 내부의 레이블을 탭하면 작동하지 않습니다. 실제로 Jason의 답변은 내가 필요한 것에 완벽하게 작동합니다.
윌리엄 T.

그러나 이것은 당신의 문제에 대한 답이 아닙니다. 이것은 둘 중 하나입니다. 기본적으로 테이블 뷰를 터치하면 제스처 인식기가 중요하지 않습니다. 원하는 동작 인식기와 테이블 뷰 선택이 모두 필요합니다.
PostCodeism

5
@Durgaprasad 셀을 선택할 때 제스처 인식기를 명시 적으로 비활성화하려면 자체 를 제외 ![touch.view isEqual: self.tableView] &&하기 위해 앞에 a 를 추가했습니다 ( 인수 뷰가 수신자 뷰 자체 인 경우 반환 되기 때문에 ). 희망은 같은 결과를 원하는 다른 사람들에게 도움이됩니다. [touch.view isDescendantOfView: self.tableView]tableViewisDescendantOfView:YES
anneblue

220

이 문제를 해결하는 가장 쉬운 방법은 다음과 같습니다.

UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
    initWithTarget:self action:@selector(tap:)];
[tapRec setCancelsTouchesInView:NO];

이를 통해 UIGestureRecognizer탭을 인식하고 다음 응답자에게 터치를 전달할 수 있습니다 . 이 방법의 의도하지 않은 결과는UITableViewCell 는 다른 뷰 컨트롤러를 푸시하는 화면 입니다. 사용자가 행을 탭하여 키보드를 닫으면 키보드와 푸시가 모두 인식됩니다. 나는 이것이 당신이 의도 한 것 같지 않지만이 방법은 많은 상황에 적합합니다.

또한 Robert의 답변을 확장하여 문제의 테이블 뷰에 대한 포인터가 있으면 문자열로 변환하지 않고 클래스를 직접 비교할 수 있으며 Apple이 명명법을 변경하지 않기를 바랍니다.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
     shouldReceiveTouch:(UITouch *)touch
{
    if([touch.view class] == tableview.class){
        return //YES/NO
    }

    return //YES/NO

}

또한 UIGestureRecognizer이 코드가 포함 된 델리게이트를 선언해야 합니다.


4
감사합니다, setCancelsTouchesInView는 모든 것을 변경합니다 :)
PostCodeism

하위 뷰를 두드리고있을 수도 있으므로 동일 isDescendantOfView여부를 확인 하는 대신 여전히 사용하고 싶을 class수 있습니다. 필요한 것에 따라 다릅니다.
shim

71

cancelsTouchesInView인식기 설정 을 false로 설정 하십시오. 그렇지 않으면 터치 자체를 "소비"하고 테이블보기로 전달하지 않습니다. 선택 이벤트가 발생하지 않는 이유입니다.

예를 들어 swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)

1
고마워요!
Bishan

1
이것은 스위프트에서 가장 깨끗합니다. 감사합니다
Dx_

사랑스럽고 간단한 테이블 뷰의 셀 내에서 잘 작동합니다!
Abdoelrhman

@laoyur 매우 사실
bLacK hoLE

아직 세포 대신 탭 제스처 클릭 있어요
famfamfam을

42

그리고 Swift의 경우 (@Jason의 답변을 기반으로 함) :

class MyAwesomeClass: UIViewController, UIGestureRecognizerDelegate

private var tap: UITapGestureRecognizer!

override func viewDidLoad() {
   super.viewDidLoad()

   self.tap = UITapGestureRecognizer(target: self, action: "viewTapped:")
   self.tap.delegate = self
   self.view.addGestureRecognizer(self.tap)
}

// UIGestureRecognizerDelegate method
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view?.isDescendantOfView(self.tableView) == true {
        return false
    }
    return true
}

고마워이 시간이 절약되었습니다.
Bishan

22

테이블 뷰 위에 탭 제스처를 추가하는 동시에 셀 선택을 허용하는 더 나은 솔루션이있을 수 있습니다.

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if gestureRecognizer is UITapGestureRecognizer {
        let location = touch.locationInView(tableView)
        return (tableView.indexPathForRowAtPoint(location) == nil)
    }
    return true
}

사용자가 탭하는 화면 지점에서 셀을 찾습니다. 인덱스 경로가 없으면 제스처가 터치를 받도록하고 그렇지 않으면 취소합니다. 나를 위해 그것은 잘 작동합니다.


1
이 솔루션은 흔들린다! 셀을 테이블의 빈 필드와 구별하는 데 매우 유용합니다.
Alessandro Ornano

18

필자는 단순히 제스처 객체 cancelsTouchesInViewfalse대해 설정된 코드 블록을 작성할 필요가 없다고 생각 합니다. 기본적 으로 코드 블록 true만 설정하면 false됩니다. 당신이 사용하는 경우 UITapGesture사용 또한 코드에서 개체를하고 UIScrollView(있는 tableview, collectionview를) 다음이 속성을 설정false

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)

이 답변을 게시 해 주셔서 감사합니다
Gustavo Baiocchi Costa

14

비슷한 해결책은 gestureRecognizer:shouldReceiveTouch:뷰의 클래스를 사용하여 구현할 조치를 결정하는 것입니다. 이 방법은 테이블을 직접 둘러싼 영역에서 탭을 마스킹하지 않는 이점이 있습니다 (이 영역의 뷰는 여전히 UITableView 인스턴스에서 내려 오지만 셀을 나타내지는 않습니다).

또한 추가 뷰를 추가하지 않고 단일 뷰에서 여러 테이블을 사용할 수 있다는 보너스도 있습니다.

경고 : Apple이 클래스 이름을 변경하지 않을 것이라는 가정이 있습니다.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}

1
클래스 이름의 문자열을 제거하려면이 줄을 사용하십시오return ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

들어 스위프트 4.2 솔루션은 구현하는 것이었다 UIGestureRecognizerDelegate하고 다음을 추가합니다 :

extension ViewController : UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view!.isDescendant(of: tblView) {
            return false
        }
        return true
    }
}

테이블 뷰를 클릭하면이 대리자 메서드가 false를 반환하고 didSelectRowAtIndexPath테이블 뷰 메서드가 제대로 작동합니다.


1
이 답변을 게시 해 주셔서 감사합니다 친구, 당신은 내 시간을 절약 할 수 있습니다.
Jay Bhalani

4

사용자 가 테이블보기 외부 를 탭했을 때만 터치 제스처 기능을 호출하려는 상황이 다릅니다 . 사용자가 테이블 뷰 내부를 두드리면 터치 제스처 기능이 호출되지 않아야합니다. 또한 터치 제스처 기능이 호출 된 경우 터치 이벤트를 소비하지 않고 탭된 뷰로 계속 전달해야합니다.

결과 코드는 Abdulrahman Masoud의 답변과 Nikolaj Nielsen의 답변을 조합 한 것입니다.

extension MyViewController: UIGestureRecognizerDelegate {
    func addGestureRecognizer() {
        let tapOnScreen = UITapGestureRecognizer(target: self,
                                                action: #selector(functionToCallWhenUserTapsOutsideOfTableView))

        // stop the gesture recognizer from "consuming" the touch event, 
        // so that the touch event can reach other buttons on view.
        tapOnScreen.cancelsTouchesInView = false

        tapOnScreen.delegate = self

        self.view.addGestureRecognizer(tapOnScreen)
    }

    // if your tap event is on the menu, don't run the touch event.
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view?.isDescendant(of: self.tableView) == true {
            return false
        }
        return true
    }

    @objc func functionToCallWhenUserTapsOutsideOfTableView() {

        print("user tapped outside table view")
    }
}

그리고 MyViewController클래스에서을 가진 클래스는 다음 UITableView과 같이 onViewDidLoad()호출했습니다 addGestureRecognizer().

class MyViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.addGestureRecognizer()
        ...
    }
    ...
}

3

그냥 설정 cancels touches in viewfalse밑에서 어떤 제스처 (table/collection)View:

-인터페이스 빌더

인터페이스 빌더

-코드

<#gestureRecognizer#>.cancelsTouchesInView = false

didSelectRowAt이 작동하지 않을뿐만 아니라 작동하지 않으려면 어떻게해야합니까?
Eyad Shokry

1

늦었고 많은 사람들이 위의 제안이 잘 작동한다는 것을 알지만 Jason 또는 TMilligan의 방법을 사용할 수 없었습니다.

숫자 키보드 만 사용하여 숫자 입력을받는 textFields를 포함하는 여러 셀 이있는 정적 tableView가 있습니다. 이것은 나에게 이상적이었다.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if(![touch.view isKindOfClass:[UITableViewCell class]]){

        [self.firstTF resignFirstResponder];
        [self.secondTF resignFirstResponder];
        [self.thirdTF resignFirstResponder];
        [self.fourthTF resignFirstResponder];

        NSLog(@"Touches Work ");

        return NO;
    }
    return YES;
}

<UIGestureRecognizerDelegate>.h 파일에서 이를 구현했는지 확인하십시오 .

이 줄 ![touch.view isKindOfClass:[UITableViewCell class]]은 tableViewCell이 탭되었는지 확인하고 활성 키보드를 닫습니다.


1

간단한 솔루션은 UITableViewCell에서 UIControl 인스턴스를 사용하여 연락을 얻는 것입니다. UIControl에 userInteractionEnables == NO 인 뷰를 추가하여 탭을 얻을 수 있습니다.


0

다음은 인식기의 shouldReceiveTouch를 키보드가 표시되는지 여부에 직접 연결하는 솔루션입니다.

탭 제스처 인식기 델리게이트에서 :

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([PFXKeyboardStateListener sharedInstance].visible) {
        return YES;
    }

    return NO;
}

그리고 PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

@property (nonatomic, readonly, getter=isVisible) BOOL visible;

@end

그리고 PFXKeyboardStateListener.m:

static PFXKeyboardStateListener *sharedInstance;

@implementation PFXKeyboardStateListener

+ (PFXKeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    @autoreleasepool {
        sharedInstance = [[self alloc] init];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

키보드 리스너의 싱글 톤 패턴을 업데이트하고 싶을 수도 있습니다. 아직 얻지 못했습니다. 이것이 나를 위해뿐만 아니라 다른 모든 사람들에게도 효과가 있기를 바랍니다. ^^


0

UIGestureRecognizer의 위임을 위해이 방법을 구현하십시오.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
{
  UIView *superview = touch.view;
  do {
    superview = superview.superview;
    if ([superview isKindOfClass:[UITableViewCell class]])
      return NO;
  } while (superview && ![superview isKindOfClass:[UITableView class]]);

  return superview != nil;
}

0

신속하게 당신은 이것을 안으로 사용할 수 있습니다

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if CheckTheTime() == true {
        // do something 
    }else{
    }
}

func CheckTheTime() -> Bool{
    return true
}

0

내 경우는 self.view의 uisearchbar 및 uitableview를 포함하여 달랐습니다. 뷰를 터치하여 uisearchbar 키보드를 닫고 싶었습니다.

var tapGestureRecognizer:UITapGestureRecognizer?

override func viewWillAppear(animated: Bool) {
    tapGestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
}

UISearchBar 델리게이트 메소드에서 :

func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
    view.addGestureRecognizer(tapGestureRecognizer!)
    return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
    view.removeGestureRecognizer(tapGestureRecognizer!)
    return true
}

사용자가 self.view를 터치하면 :

func handleTap(recognizer: UITapGestureRecognizer) {
    sampleSearchBar.endEditing(true)
    sampleSearchBar.resignFirstResponder()
}

0

2020 년 5 월 스위프트 5

텍스트를 입력하면 표시되는 textField 및 tableView가 있습니다.

초기 상태 그래서 tableViewCell 또는 다른 것을 탭하면 두 가지 이벤트가 필요합니다.

키보드와 tableView가 표시되고 있습니다

먼저 tapGestureRecognizer를 추가합니다.

tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
tap.delegate = self
view.addGestureRecognizer(tap)

@objc func viewTapped() {
        view.endEditing(true)
}

그런 다음 UIGestureRecognizerDelegate에 다음 검사를 추가합니다.

extension StadtViewController: UIGestureRecognizerDelegate {

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view?.isDescendant(of: self.tableView) == true {
        return false
    } else {
        view.endEditing(true)
        return true
    }
}

}

키보드를 먼저 숨기려면 tableView가 계속 표시되고 탭에 응답합니다.

여기에 이미지 설명을 입력하십시오


-1

이것은 위의 답변을 기반으로 한 내 솔루션입니다 ... 그것은 나를 위해 일했습니다 ...

//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];

- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
    //Write your code here
}

-1

문제 : 필자의 경우 문제는 원래 각 collectionView 셀에 버튼을 배치하고 셀을 채우기 위해 제약 조건을 설정하여 셀을 클릭 할 때 버튼을 클릭했지만 버튼 기능은 비어 있었기 때문에 아무것도 없었습니다. 일어나고있는 것처럼 보입니다.

FIX : 컬렉션 뷰 셀에서 버튼을 제거하여 수정했습니다.

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