UITapGestureRecognizerがUITableViewを壊しましたdidSelectRowAtIndexPath


207

キーボードが表示されたときにテキストフィールドを上にスクロールする独自の関数を記述しました。テキストフィールドから離れてタップしてキーボードを閉じるために、タップして離れたときにテキストフィールドでUITapGestureRecognizerファーストレスポンダーを辞任する処理を行うを作成しました。

ここでUITableView、テキストフィールドのすぐ下に作成し、ユーザーがテキストを入力するとアイテムが入力されるテキストフィールドのオートコンプリートも作成しました。

ただし、自動補完テーブルのエントリの1つを選択すると、didSelectRowAtIndexPathは呼び出されません。代わりに、タップジェスチャレコグナイザが呼び出され、ファーストレスポンダを辞任するようです。

タップジェスチャー認識機能にタップメッセージをに渡し続けるように指示する方法がいくつかあるUITableViewと思いますが、それが何であるか理解できません。任意の助けをいただければ幸いです。


このスレッドを参照してください。
Duncan Babbage

回答:


258

OK、ジェスチャーレコグナイザードキュメントを検索した結果、ようやく見つかりました。

解決策は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]];
でした

1
@Joshこれは、セル内のラベルをタップすると機能しません。実際、ジェイソンの答えは私が必要とするものにぴったりです。
ウィリアムT.

しかし、これはあなたの問題の答えではありませんか?これはどちらかまたは両方です。基本的に、テーブルビューへのタッチはジェスチャー
レコグナイザー

5
@Durgaprasad 以前に追加したセルを選択するときにジェスチャ認識機能を明示的に 無効![touch.view isEqual: self.tableView] &&にし[touch.view isDescendantOfView: self.tableView]て、tableViewそれ自体を除外します(argument-viewがレシーバービュー自体の場合isDescendantOfView:も返さYESれるため)。同じ結果を望んでいる他の人々を助けることを願っています。
anneblue 2015

220

この問題を解決する最も簡単な方法は、次のとおりです。

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

これにより、UIGestureRecognizerタップが認識され、タッチが次のレスポンダに渡されます。このメソッドの意図しない結果は、UITableViewCell画面上に別のビューコントローラーをプッシュする場合です。ユーザーが行をタップしてキーボードを非表示にすると、キーボードとプッシュの両方が認識されます。これがあなたの意図していることだとは思いませんが、この方法は多くの状況に適しています。

また、ロバートの答えを拡張すると、問題のテーブルビューへのポインターがある場合、文字列に変換する必要がなく、そのクラスを直接比較して、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あります。必要なものによって異なります。
シム2016年

71

cancelsTouchesInView認識機能をfalseに設定します。それ以外の場合は、それ自体がタッチを「消費」し、それをテーブルビューに渡しません。そのため、選択イベントは発生しません。

たとえば swift

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

1
これはたくさんありがとう!
Bishan

1
これはSwiftで最もクリーンです。ありがとう
Dx_ 16

テーブルビューのセル内でうまく機能し、美しくシンプルです!
Abdoelrhman 2017年

@laoyurは非常に真実
bLacKホール2017年

依然としてタップジェスチャーではなくセルクリックを取得
famfamfam 2018年

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
このソリューションは素晴らしいです!テーブルの空のフィールドからの細胞を区別することは非常に便利...
アレッサンドロオルナーノ

18

私は単にジェスチャーオブジェクトに設定cancelsTouchesInViewするだけのコードブロックを書く必要はないと思い falseます。デフォルトで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インスタンスから派生していますが、セルを表していません)。

これには、(余分なコードを追加せずに)1つのビューで複数のテーブルを操作できるという利点もあります。

注意: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

別の状況で、ユーザーがテーブルビューの外側をタップしたときにのみタッチジェスチャー関数を呼び出したいと思っていました。ユーザーがテーブルビュー内をタップした場合、タッチジェスチャー機能を呼び出さないでください。さらに、タッチジェスチャー関数が呼び出された場合でも、タップイベントを消費するのではなく、タップされたビューに渡します。

結果のコードは、アブドゥルラフマンマソウドの回答とニコライニールセンの回答を組み合わせたものです。

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 viewするだけです:false(table/collection)View

-Interfacebuilder

インターフェースビルダー

-コード

<#gestureRecognizer#>.cancelsTouchesInView = false

無効にしたい場合は、didSelectRowAtが機能するだけでなく、
Eyad Shokry

1

遅いですが、多くの人が上記の提案がうまく機能することに気づきましたが、ジェイソンまたはTMilliganの方法を機能させることができませんでした。

私が持っている静的な数だけキーボードを使って数値入力を受け取るテキストフィールドを含む複数のセルとの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インスタンスを使用してタッチを取得することです。userInteractionEnables == NOのビューをUIControlに追加して、タップを取得できます。


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

私の場合は、uisearchbarとself.viewの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月のSwift 5。

テキストを入力すると表示されるtextFieldとtableViewがあります。

初期状態 したがって、tableViewCellなどをタップすると、2つの異なるイベントが必要になります。

キーボードと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セルにボタンを配置し、セルを埋めるように制約を設定したため、セルをクリックするとボタンがクリックされましたが、ボタンの機能が空だったため、何もありませんでした起こっているように見えます。

修正:コレクションビューのセルからボタンを削除してこれを修正しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.