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];
Matrosov Alexander
quelle
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
}
Ravi
quelle
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
}
Ian
quelle
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.

Antoine
quelle
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.

Imanou Petit
quelle
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
        }
    }
}
Arru
quelle
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
  }
}
CoachThys
quelle
4

Schneller Weg

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}
Musa almatri
quelle
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
}
Rico
quelle
2

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

Geben Sie hier die Bildbeschreibung ein

MrDEV
quelle
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
     }
Shay Moreno
quelle
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
}
Jonathan Cabrera
quelle
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)
}
PDK
quelle