UITapGestureRecognizer unterbricht UITableView didSelectRowAtIndexPath


207

Ich habe meine eigene Funktion geschrieben, um Textfelder nach oben zu scrollen, wenn die Tastatur angezeigt wird. Um die Tastatur durch Tippen auf das Textfeld zu schließen, habe ich eine erstellt UITapGestureRecognizer, die dafür sorgt, dass der Ersthelfer beim Tippen auf das Textfeld zurücktritt.

Jetzt habe ich auch eine automatische Vervollständigung für das Textfeld erstellt, die UITableViewdirekt unter dem Textfeld erstellt und mit Elementen gefüllt wird, wenn der Benutzer Text eingibt.

Wenn Sie jedoch einen der Einträge in der automatisch vervollständigten Tabelle auswählen, didSelectRowAtIndexPathwird dieser nicht aufgerufen. Stattdessen scheint es, dass der Tippgestenerkenner aufgerufen wird und nur den Ersthelfer zurücktritt.

Ich vermute, es gibt eine Möglichkeit, dem Tap-Gestenerkenner zu sagen, dass er die Tap-Nachricht weiter an den weiterleiten soll UITableView, aber ich kann nicht herausfinden, was es ist. Jede Hilfe wäre sehr dankbar.


Antworten:


258

Ok, endlich gefunden nach einigem Durchsuchen der Gestenerkennungsdokumente.

Die Lösung bestand darin UIGestureRecognizerDelegate, Folgendes zu implementieren und hinzuzufügen:

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

Das hat sich darum gekümmert. Hoffentlich hilft das auch anderen.


Sie müssen wahrscheinlich auch das Tippen auf das Textfeld überspringen.
Steven Kramer

5
Ich hatte mehr Glück mitreturn ![touch.view isKindOfClass:[UITableViewCell class]];
Josh Paradroid

1
@Josh Dies funktioniert nicht, wenn Sie auf die Beschriftungen in der Zelle tippen. Eigentlich funktioniert Jasons Antwort perfekt für das, was ich brauche.
William T.

Aber das ist nicht die Antwort auf Ihr Problem, nein? Dies ist ein entweder / oder. Grundsätzlich ist jede Berührung der Tabellenansicht mit dem Gestenerkenner unbedeutend. Was Sie wollen, ist, dass sowohl der Gestenerkenner als auch die Auswahl der Tabellenansicht funktionieren
PostCodeism

5
@Durgaprasad Um die Gestenerkennung bei Auswahl der Zellen explizit zu deaktivieren , habe ich eine ![touch.view isEqual: self.tableView] &&vor dem hinzugefügt [touch.view isDescendantOfView: self.tableView], um das Selbst auszuschließen tableView(da isDescendantOfView:auch zurückgegeben wird, YESwenn die Argumentansicht die Empfängeransicht selbst ist). Hoffe, das hilft anderen Menschen, die das gleiche Ergebnis wollen.
Anneblue

220

Der einfachste Weg, um dieses Problem zu lösen, ist:

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

Dadurch kann der UIGestureRecognizerBenutzer das Tippen erkennen und die Berührung auch an den nächsten Antwortenden weitergeben. Eine unbeabsichtigte Folge dieser Methode ist, wenn Sie einen UITableViewCellBildschirm haben, der einen anderen Ansichts-Controller drückt. Wenn der Benutzer auf die Zeile tippt, um die Tastatur zu schließen, werden sowohl die Tastatur als auch der Druck erkannt. Ich bezweifle, dass Sie dies beabsichtigen, aber diese Methode ist für viele Situationen angemessen.

Wenn Sie die Antwort von Robert erweitern und einen Zeiger auf die betreffende Tabellenansicht haben, können Sie die Klasse direkt vergleichen, anstatt sie in eine Zeichenfolge konvertieren zu müssen, und hoffen, dass Apple die Nomenklatur nicht ändert:

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

    return //YES/NO

}

Denken Sie daran, dass Sie auch deklarieren müssen UIGestureRecognizer, dass ein Delegat mit diesem Code darin enthalten ist.


4
Danke, setCancelsTouchesInView ändert alles :)
PostCodeism

Möglicherweise möchten Sie immer noch verwenden, isDescendantOfViewanstatt nur zu überprüfen, ob das classgleich ist, da Sie möglicherweise auf eine Unteransicht tippen. Kommt darauf an was du brauchst.
Shim

71

Setzen Sie cancelsTouchesInViewIhren Erkenner auf false. Andernfalls "verbraucht" es die Berührung für sich selbst und gibt sie nicht an die Tabellenansicht weiter. Deshalb findet das Auswahlereignis nie statt.

zum Beispiel in swift

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

1
Vielen Dank dafür!
Bishan

1
Dies ist das sauberste in Swift. Danke
Dx_

funktioniert gut in Zellen in der Tabellenansicht, schön und einfach!
Abdoelrhman

@laoyur sehr wahr
bLacK hoLE


42

Und für Swift (basierend auf der Antwort von @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
}

Vielen Dank, das hat mir Zeit gespart.
Bishan

22

Ich habe möglicherweise eine bessere Lösung, um eine Tippgeste über einer Tabellenansicht hinzuzufügen, aber gleichzeitig die Zellenauswahl zuzulassen:

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

Ich suche nur nach einer Zelle an der Stelle des Bildschirms, auf die der Benutzer tippt. Wenn kein Indexpfad gefunden wird, lasse ich die Geste die Berührung empfangen, andernfalls brich ich sie ab. Bei mir funktioniert es super.


1
Diese Lösung rockt! Sehr nützlich, um die Zelle vom leeren Feld der Tabelle zu unterscheiden.
Alessandro Ornano

18

Ich denke, es ist nicht nötig, Codeblöcke zu schreiben, die einfach cancelsTouchesInViewauf falseIhr Gestenobjekt eingestellt sind. Standardmäßig ist dies der Fall, trueund Sie müssen es nur festlegen false. Wenn Sie ein UITapGestureObjekt in Ihrem Code verwenden und auch UIScrollView(Tabellenansicht, Sammlungsansicht) verwenden, legen Sie diese Eigenschaft festfalse

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

Vielen Dank für die Veröffentlichung dieser Antwort
Gustavo Baiocchi Costa

14

Eine ähnliche Lösung ist die Implementierung gestureRecognizer:shouldReceiveTouch: mithilfe der Klasse der Ansicht , um zu bestimmen, welche Aktion ausgeführt werden soll. Dieser Ansatz hat den Vorteil, dass Abgriffe in dem Bereich, der die Tabelle direkt umgibt, nicht maskiert werden (die Ansichten dieses Bereichs stammen immer noch von den UITableView-Instanzen ab, stellen jedoch keine Zellen dar).

Dies hat auch den Vorteil, dass es mit mehreren Tabellen in einer einzigen Ansicht funktioniert (ohne zusätzlichen Code hinzuzufügen).

Vorsichtsmaßnahme: Es wird davon ausgegangen, dass Apple den Klassennamen nicht ändert.

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

1
Verwenden Sie diese Zeilereturn ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang

7

Für Swift 4.2 bestand die Lösung darin UIGestureRecognizerDelegate, Folgendes zu implementieren und hinzuzufügen:

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

Wenn Sie auf Tabellenansicht klicken, gibt diese Delegatenmethode false zurück und die didSelectRowAtIndexPathMethode der Tabellenansicht funktioniert ordnungsgemäß.


1
Danke Kumpel für das Posten dieser Antwort, du sparst meine Zeit.
Jay Bhalani

4

Ich hatte eine andere Situation, in der ich wollte, dass die Touch-Gestenfunktion nur aufgerufen wird , wenn der Benutzer außerhalb der Tabellenansicht tippt. Wenn der Benutzer in die Tabellenansicht getippt hat, sollte die Touch-Gestenfunktion nicht aufgerufen werden. Wenn die Touch-Gestenfunktion aufgerufen wird, sollte das Touch-Ereignis weiterhin an die Ansicht übergeben werden, auf die getippt wurde, anstatt es zu verbrauchen.

Der resultierende Code ist eine Kombination aus Abdulrahman Masouds Antwort und Nikolaj Nielsens Antwort.

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")
    }
}

Und in der MyViewControllerKlasse, die die Klasse hat die UITableView, in der onViewDidLoad(), stellte ich sicher , zu nennen addGestureRecognizer():

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

3

Stellen Sie einfach cancels touches in viewauf falsefür jede Geste unter der (table/collection)View:

- Interfacebuilder

Interfacebuilder

- Code

<#gestureRecognizer#>.cancelsTouchesInView = false

Was ist, wenn ich die Arbeit deaktivieren möchte, damit nicht nur didSelectRowAt damit arbeiten kann?
Eyad Shokry

1

Obwohl es spät ist und viele Leute feststellen, dass die oben genannten Vorschläge gut funktionieren, konnte ich Jasons oder TMilligans Methoden nicht zum Laufen bringen.

Ich habe eine statische Tabellenansicht mit mehreren Zellen, die textFields enthalten, die Zahleneingaben nur über die Zifferntastatur empfangen. Das war ideal für mich:

- (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;
}

Stellen Sie sicher, dass Sie dies <UIGestureRecognizerDelegate>in Ihrer .h-Datei implementiert haben .

Diese Zeile ![touch.view isKindOfClass:[UITableViewCell class]]prüft, ob auf eine tableViewCell getippt wurde, und schließt eine aktive Tastatur aus.


1

Eine einfache Lösung besteht darin, UIControl-Instanzen in UITableViewCell zu verwenden, um Berührungen zu erhalten. Sie können UIControl beliebige Ansichten mit userInteractionEnables == NO hinzufügen, um Abgriffe abzurufen.


0

Hier ist meine Lösung, die das shouldReceiveTouch des Erkenners direkt damit verknüpft, ob die Tastatur angezeigt wird.

In Ihrem Tap-Gestenerkennungs-Delegaten:

#pragma mark - UIGestureRecognizerDelegate

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

    return NO;
}

Und die PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

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

@end

Und die 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

Möglicherweise möchten Sie das Singleton-Muster des Tastatur-Listeners aktualisieren. Ich bin noch nicht dazu gekommen. Hoffe, das funktioniert für alle anderen genauso wie für mich. ^^


0

Implementieren Sie diese Methode für den Delegaten von 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

In Kürze können Sie dies im Inneren verwenden

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

func CheckTheTime() -> Bool{
    return true
}

0

Mein Fall war anders, einschließlich einer Suchleiste und einer geeigneten Ansicht auf self.view. Ich wollte die Tastatur der Suchleiste durch Berühren der Ansicht schließen.

var tapGestureRecognizer:UITapGestureRecognizer?

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

Zu UISearchBar-Delegatmethoden:

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

Wenn der Benutzer self.view berührt:

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

0

Swift 5, Mai 2020.

Ich habe ein Textfeld und eine Tabellenansicht, die sichtbar werden, wenn ich Text eingebe.

Ausgangszustand Ich möchte also zwei verschiedene Ereignisse, wenn ich auf tableViewCell oder etwas anderes tippe.

Tastatur und tableView werden angezeigt

Zuerst fügen wir tapGestureRecognizer hinzu.

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

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

Anschließend fügen wir UIGestureRecognizerDelegate den folgenden Check hinzu:

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

}}

Wenn ich zuerst die Tastatur ausblenden möchte, bleibt die tableView sichtbar und reagiert auf meine Berührungen.

Geben Sie hier die Bildbeschreibung ein


-1

Dies ist meine Lösung basierend auf den obigen Antworten ... Es hat für mich funktioniert ...

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

PROBLEM: In meinem Fall bestand das Problem darin, dass ich ursprünglich eine Schaltfläche in jede collectionView-Zelle eingefügt und die Einschränkungen zum Füllen der Zelle festgelegt habe, sodass beim Klicken auf die Zelle auf die Schaltfläche geklickt wurde, die Schaltflächenfunktion jedoch leer war und nichts vorhanden war scheinbar passiert.

UPDATE: Ich habe dies behoben, indem ich die Schaltfläche aus der Zelle der Sammlungsansicht entfernt habe.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.