IT story

UITapGestureRecognizer가 UITableView를 중단 함 didSelectRowAtIndexPath

hot-time 2020. 5. 11. 08:04
반응형

UITapGestureRecognizer가 UITableView를 중단 함 didSelectRowAtIndexPath


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

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

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

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


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

해결책은 다음을 구현 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;
}

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


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

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이 코드가 포함 된 델리게이트를 선언해야 합니다.


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

예를 들어 swift

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

그리고 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
}

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

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

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


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

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

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

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

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

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

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

결과 코드는 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()
        ...
    }
    ...
}

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

-인터페이스 빌더

인터페이스 빌더

-코드

<#gestureRecognizer#>.cancelsTouchesInView = false

들어 스위프트 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테이블 뷰 메서드가 제대로 작동합니다.


늦었고 많은 사람들이 위의 제안이 잘 작동한다는 것을 알지만 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이 탭되었는지 확인하고 활성 키보드를 닫습니다.


Simple solution is using UIControl instances in UITableViewCell to get touches. You can add any views with userInteractionEnables == NO to UIControl to get taps.


Here is my solution, which ties the recognizer's shouldReceiveTouch directly to whether the keyboard is showing.

In your tap gesture recognizer delegate:

#pragma mark - UIGestureRecognizerDelegate

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

    return NO;
}

And the PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

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

@end

And the 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

You may want to update the singleton pattern of the keyboard listener, I haven't gotten to it yet. Hope this works for everyone else as well as it works for me. ^^


Implement this method for delegate of 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;
}

In swift you can use this inside

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

func CheckTheTime() -> Bool{
    return true
}

My case was different including a uisearchbar and uitableview on self.view. I wanted to dismiss uisearchbar keyboard by touching on the view.

var tapGestureRecognizer:UITapGestureRecognizer?

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

On UISearchBar Delegate Methods:

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

When user touches on self.view:

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

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

//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
}

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

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

참고 URL : https://stackoverflow.com/questions/8192480/uitapgesturerecognizer-breaks-uitableview-didselectrowatindexpath

반응형