UITapGestureRecognizer tippen Sie auf self.view, ignorieren Sie jedoch Unteransichten


93

Ich muss eine Funktion implementieren, die Code aufruft, wenn ich zweimal auf die self.view (Ansicht von UIViewCotroller) tippe . Aber das Problem, dass ich ein anderes UI-Objekt in dieser Ansicht habe und kein Erkennungsobjekt an alle anhängen möchte. Ich habe diese Methode unten gefunden, um Gesten in meiner Ansicht zu machen, und ich weiß, wie es funktioniert. Im Moment bin ich vor dem Handicap, welchen Weg ich wählen soll, um diesen Erkenner zu erstellen, ohne die Unteransicht zu beachten. Irgendwelche Ideen? Vielen Dank.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];

1
Ich bin nicht sicher, aber haben Sie versucht, cancelsTouchesInView auf dem Erkenner auf NO zu setzen? also [doubleTap setCancelsTouchesInView: NO];
JDx

Antworten:


142

Sie sollten das UIGestureRecognizerDelegateProtokoll im selfObjekt übernehmen und die folgende Methode zum Überprüfen der Ansicht aufrufen. Überprüfen Sie bei dieser Methode Ihre Ansicht mit touch.viewund geben Sie den entsprechenden Bool zurück (Ja / Nein). Etwas wie das:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:yourSubView]) {
        return NO;
    }
    return YES;
}

Bearbeiten: Bitte überprüfen Sie auch @ Ians Antwort!

Swift 5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

Noch eine Frage Ich habe einige Schaltflächen in meiner Ansicht, die funktionieren müssen. Wie kann ich ihnen Priorität einräumen?
Matrosov Alexander

Wenn Ihre Schaltflächen ihre eigenen Gestenerkenner haben (wahrscheinlich), stöbern Sie in ihren Interna und greifen Sie nach ihnen und lesen Sie die Dokumente weiter, -[UIGestureRecognizer requireGestureRecognizerToFail:]aber es könnte funktionieren, ohne sie zu ändern
bshirley

habe stundenlang danach gesucht! Vielen Dank! ;)
MasterRazer

Wenn Ihr ifTest fehlschlägt, gibt Ihre Implementierung kein BOOL zurück. Für den richtigen Stil geben Sie JA nach einem if-Block (using { }) oder in einem elseZweig zurück. Vielen Dank, ersparte mir ein bisschen Lesen.
RobP

2
Eine Ansicht ist ein Nachkomme von sich selbst, daher funktioniert dies nicht ... (der allgemeine Ansatz ist jedoch in
Ordnung

107

Ein anderer Ansatz besteht darin, nur zu vergleichen, ob die Ansicht der Berührung die Gestenansicht ist, da Nachkommen die Bedingung nicht erfüllen. Ein schöner, einfacher Einzeiler:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}

2
Das funktioniert bei mir besser als die akzeptierte Antwort. Viel prägnanter.
Suman Roy

1
@Ian Diese Methode wird beim Tippen auf die Ansicht nicht aufgerufen.
Praburaj

1
Tolle prägnante Antwort!
Scurioni

Die akzeptierte Antwort hat nicht einmal funktioniert, aber das hat bei mir einwandfrei funktioniert.
Shalin Shah

@ Prabu wahrscheinlich, weil der Delegierte des Gestenerkenners vorher eingestellt werden muss
Kubba

23

Und für die Swift-Variante:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOfView(yourSubView){
        return false
    }
    return true
}

Gut zu wissen, isDescendantOfViewgibt einen BooleanWert zurück, der angibt, ob der Empfänger eine Unteransicht einer bestimmten Ansicht ist oder mit dieser Ansicht identisch ist.


1
Vergessen Sie nicht, das UIGestureRecognizerDelegate in Ihrer Klasse festzulegen!
Fox5150

4
Können Sie diese if-Anweisung nicht einfach zurückgeben, anstatt sie zu tun? return! touch.view.isDescendant (von: gestureRecognizer.view) Auch neue Syntax in Swift 3 ^^
EdwardSanchez

12

Mit Swift 5 und iOS 12 UIGestureRecognizerDelegatewird eine Methode aufgerufen gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)hat die folgende Erklärung:

Fragen Sie den Delegierten, ob ein Gestenerkenner ein Objekt erhalten soll, das eine Berührung darstellt.

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

Der vollständige Code unten zeigt eine mögliche Implementierung für gestureRecognizer(_:shouldReceive:). Mit diesem Code wird durch Tippen auf eine Unteransicht der ViewControllerAnsicht (einschließlich imageView) die printHello(_:)Methode nicht ausgelöst .

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view's gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

Eine alternative Implementierung von gestureRecognizer(_:shouldReceive:)kann sein:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}

Beachten Sie jedoch, dass dieser alternative Code nicht prüft, ob touch.viewes sich um eine Unteransicht von handelt gestureRecognizer.view.


10

Komplette schnelle Lösung (Delegat muss implementiert UND für Erkenner festgelegt sein):

class MyViewController: UIViewController UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.delegate = self
        self.view.addGestureRecognizer(doubleTapRecognizer)
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
        if touch.view.isDescendantOfView(self.view){
            return false
        }
        return true
    }

    func onBaseTapOnly(sender: UITapGestureRecognizer) {
        if sender.state == .Ended {
            //react to tap
        }
    }
}

6

Variante mit CGPoint, die Sie berühren (SWIFT 4.0)

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {

// Get the location in CGPoint
    let location = touch.location(in: nil)

// Check if location is inside the view to avoid
    if viewToAvoid.frame.contains(location) {
        return false
    }

    return true
  }
}

4

Schneller Weg

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}

Wird in iOS 13 niemals aufgerufen, zumindest nicht mit swipeGestureRecognizer.
Chewie The Chorkie

Wie unterscheidet sich das von Ians Antwort?
Sweetfa

3

Beachten Sie, dass die gestureRecognizer-API geändert wurde in:

gestureRecognizer (_: shouldReceive :)

Beachten Sie insbesondere den Unterstrich (Überspringen) für die externe Beschriftung des ersten Parameters.

Anhand vieler der oben aufgeführten Beispiele erhielt ich die Veranstaltung nicht. Unten finden Sie ein Beispiel, das für aktuelle Versionen von Swift (3+) funktioniert.

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var shouldReceive = false
    if let clickedView = touch.view {
        if clickedView == self.view {
            shouldReceive = true;
        }
    }
    return shouldReceive
}

2

Vergessen Sie bei den oben genannten Lösungen nicht, User Interaction EnabledIhre Unteransicht zu überprüfen .

Geben Sie hier die Bildbeschreibung ein


2

Ich musste die Geste in der Kinderansicht verhindern. Das einzige, was funktioniert hat, ist, die erste Ansicht zuzulassen und beizubehalten und Gesten in allen nächsten Ansichten zu verhindern:

   var gestureView: UIView? = nil

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (gestureView == nil || gestureView == touch.view){
            gestureView = touch.view
            return true
        }
        return false
     }

1

Swift 4:

touch.view ist jetzt optional, also basierend auf der Antwort von @ Antoine:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
        return false
    }
    return true
}

0

Wenn Sie nicht Ihren wollen ‚Doppel-Tap Erkenner‘ zu einem Konflikt mit Tasten und / oder anderen Steuerelemente können Sie einstellen , selfwie UIGestureRecognizerDelegateund implementieren:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
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.