UITapGestureRecognizer self.view'a dokunun ancak alt görünümleri yoksayın


98

Self.view (görünümü UIViewCotroller) üzerine iki kez dokunduğumda bazı kodları çağıracak bir özellik uygulamam gerekiyor . Ancak bu görünümde başka UI nesnesine sahip olmam ve hepsine herhangi bir tanıyıcı nesne eklemek istemiyorum. Bu yöntemi aşağıda kendi görüşüme göre nasıl hareket yapacağımı buldum ve nasıl çalıştığını biliyorum. Şu anda, alt görünümü yok sayarak bu tanıyıcıyı oluşturmak için hangi yolu seçeceğim konusunda bir engelin önündeyim. Herhangi bir fikir? Teşekkürler.

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

1
Emin değilim, ancak cancelsTouchesInView'ı tanıyıcıda NO olarak ayarlamayı denediniz mi? bu nedenle [doubleTap setCancelsTouchesInView: HAYIR];
JDx

Yanıtlar:


149

Nesnenin UIGestureRecognizerDelegateiçindeki protokolü benimsemeli selfve görünümü kontrol etmek için aşağıdaki yöntemi çağırmalısınız. Bu yöntemin içinde, görüşünüzü kontrol edin touch.viewve uygun bool (Evet / Hayır) ile geri dönün. Bunun gibi bir şey:

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

Düzenleme: Lütfen @ Ian'ın cevabını da kontrol edin!

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
}

bir soru daha Benim görüşümde çalışması gereken birkaç düğme var. onlar için nasıl öncelik belirleyebilirim?
Matrosov Alexander

düğmelerinizin kendi hareket tanıyıcıları varsa (muhtemelen), iç -[UIGestureRecognizer requireGestureRecognizerToFail:]kısımlarını araştırın ve onları alın ve dokümanları okuyun, ancak onları değiştirmeden de çalışabilir
bshirley

saatlerdir bunu arıyordum! Teşekkürler! ;)
MasterRazer

Senin Eğer iftest başarısız, senin uygulaması BOOL dönmek başarısız; uygun stil için bir if bloğundan sonra (kullanan { }) veya bir elsedalda YES döndür. Yine de teşekkürler, biraz kitap okumamı kurtardım.
RobP

2
Görünüm, kendisinin soyundan yani yapar değil iş ... (genel yaklaşım Tamam da, diğer yanıt bakın edilir)
Ixx

110

Başka bir yaklaşım, dokunma görünümünün hareketler görünümü olup olmadığını karşılaştırmaktır, çünkü torunlar koşulu geçemez. Hoş, basit bir tek satırlık:

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

2
Bu benim için kabul edilen cevaptan daha iyi çalışıyor. Çok daha kısa.
Suman Roy

1
@ Bu yöntem, görünüme dokunulduğunda çağrılmaz.
Praburaj

1
Harika kısa cevap!
scurioni

Kabul edilen cevap işe yaramadı bile ama bu benim için kusursuz bir şekilde çalıştı.
Shalin Şah

@Prabu muhtemelen jest tanıyan delegesinin daha önce ayarlanması gerektiğinden
Kubba

23

Ve Swift varyantı için:

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

Bilmekte isDescendantOfViewfayda var Boolean, alıcının belirli bir görünümün alt görünümü mü yoksa bu görünümle aynı mı olduğunu gösteren bir değer döndürür .


1
Sınıfınızda UIGestureRecognizerDelegate'i ayarlamayı unutmayın!
Fox5150

4
Bunu if ifadesini yapmak yerine, bunu geri veremez misin? ! (içinde: gestureRecognizer.view) dönüş touch.view.isDescendant Ayrıca ^^ swift 3 yeni sözdizimi
EdwardSanchez

15

Swift 5 ve IOS 12 ile, UIGestureRecognizerDelegateadlı bir yöntem vardır gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)aşağıdaki beyana sahiptir:

Temsilciye, bir hareket tanıyıcının bir dokunuşu temsil eden bir nesne alıp almayacağını sorun.

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

Aşağıdaki kodun tamamı, için olası bir uygulamayı gösterir gestureRecognizer(_:shouldReceive:). Bu kodla, ViewControllergörünümünün bir alt görünümüne (dahil imageView) dokunmak printHello(_:)yöntemi tetiklemez .

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

}

Alternatif bir uygulama gestureRecognizer(_:shouldReceive:)şunlar olabilir:

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

Ancak bu alternatif kodun alt touch.viewgörünümü olup olmadığını kontrol etmediğini unutmayın gestureRecognizer.view.


10

Eksiksiz hızlı çözüm (delege uygulanmalı VE tanıyıcı (lar) için ayarlanmalıdır):

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

Dokunduğunuz CGPoint kullanan varyant (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

Clear Swift yolu

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

En azından swipeGestureRecognizer ile iOS 13'te asla aranmaz.
Chewie The Chorkie

Bunun Ian'ın cevabından farkı nedir?
sweetfa

3

MotionRecognizer API'nin şu şekilde değiştiğini unutmayın :

hareketRecognizer (_: shouldReceive :)

İlk parametrenin harici etiketi için alt çizgi (atlama) göstergesine özellikle dikkat edin.

Yukarıda verilen örneklerin çoğunu kullanarak etkinliği alamadım. Aşağıda Swift'in (3+) güncel sürümleri için çalışan bir örnek bulunmaktadır.

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

Ayrıca yukarıdaki çözümler, User Interaction Enabledalt görünümünüzü kontrol etmeyi unutmayın .

görüntü açıklamasını buraya girin


2

Çocuk görünümündeki hareketi engellemek zorunda kaldım. İşe yarayan tek şey, sonraki tüm görünümlerde ilk görünüme izin vermek ve bunu korumak ve hareketi önlemektir:

   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 artık isteğe bağlıdır, dolayısıyla @ Antoine'ın cevabına göre:

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

0

Eğer düğme ve / veya diğer kontrollerle çatışma için 'çift dokunun Recogniser' istemiyorsanız, ayarlayabileceğiniz selfolarak UIGestureRecognizerDelegateve uygulamak:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.