Ketuk UITapGestureRecognizer pada self.view tetapi abaikan subview

97

Saya perlu mengimplementasikan fitur yang akan memanggil beberapa kode ketika saya mengetuk dua kali pada self.view (view of UIViewCotroller). Tetapi masalah bahwa saya memiliki objek UI lain pada tampilan ini dan saya tidak ingin melampirkan objek pengenal apa pun ke semuanya. Saya menemukan metode ini di bawah cara memberi isyarat pada pandangan saya dan saya tahu cara kerjanya. Saat ini saya berada di depan handicap, cara mana yang harus dipilih untuk membuat pengenal ini mengabaikan subview. Ada ide? Terima kasih.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];
Matrosov Alexander
sumber
1
Saya tidak yakin, tetapi apakah Anda sudah mencoba menyetel cancelsTouchesInView ke NO pada pengenalnya? jadi [doubleTap setCancelsTouchesInView: NO];
JDx

Jawaban:

147

Anda harus mengadopsi UIGestureRecognizerDelegateprotokol di dalam selfobjek dan memanggil metode di bawah ini untuk memeriksa tampilan. Di dalam metode ini, periksa tampilan Anda touch.viewdan kembalikan bool yang sesuai (Ya / Tidak). Sesuatu seperti ini:

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

Edit: Tolong, periksa juga jawaban @ Ian!

Cepat 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
sumber
satu pertanyaan lagi. Saya memiliki beberapa tombol pada pandangan saya yang harus berfungsi. bagaimana saya dapat menetapkan beberapa prioritas untuk mereka?
Matrosov Alexander
jika tombol Anda memiliki pengenal isyaratnya sendiri (kemungkinan besar), gali bagian dalamnya dan ambil, dan baca -[UIGestureRecognizer requireGestureRecognizerToFail:]dokumennya tetapi mungkin berfungsi tanpa mengubahnya
bshirley
sedang mencari ini selama berjam-jam! Terima kasih! ;)
MasterRazer
Jika ifpengujian Anda gagal, implementasi Anda gagal mengembalikan BOOL; untuk gaya yang benar, kembalikan YA setelah blok if (menggunakan { }) atau di elsecabang. Terima kasih, meskipun, menghemat sedikit bacaan saya.
RobP
2
Pandangan adalah turunan dari dirinya sendiri sehingga ini tidak berfungsi ... (pendekatan keseluruhan baik-baik saja, lihat jawaban lain)
Ixx
109

Pendekatan lain adalah dengan membandingkan jika tampilan sentuhan adalah tampilan isyarat, karena keturunan tidak akan melewati kondisi tersebut. Satu baris yang bagus dan sederhana:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}
Ian
sumber
2
Ini bekerja lebih baik bagi saya daripada jawaban yang diterima. Jauh lebih ringkas.
Suman Roy
1
@Ian metode ini tidak dipanggil saat mengetuk tampilan.
Praburaj
1
Jawaban singkat yang bagus!
scurioni
Jawaban yang diterima bahkan tidak berhasil tetapi ini bekerja dengan sempurna untuk saya.
Shalin Shah
@Prabu mungkin karena delegasi pengenal gerakan harus ditetapkan sebelumnya
Kubba
23

Dan untuk varian Swift:

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

Perlu diketahui, isDescendantOfViewmengembalikan Booleannilai yang menunjukkan apakah penerima adalah subview dari tampilan tertentu atau identik dengan tampilan tersebut.

Antoine
sumber
1
Jangan lupa untuk menyetel UIGestureRecognizerDelegate di kelas Anda!
Fox5150
4
Daripada melakukan itu jika pernyataan, tidak bisakah Anda mengembalikan ini? return! touch.view.isDescendant (dari: gestureRecognizer.view) Juga, sintaks baru di swift 3 ^^
EdwardSanchez
13

Dengan Swift 5 dan iOS 12, UIGestureRecognizerDelegateada metode yang disebut gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)memiliki deklarasi berikut:

Tanyakan kepada delegasi apakah pengenal gerakan harus menerima objek yang mewakili sentuhan.

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

Kode lengkap di bawah ini menunjukkan kemungkinan implementasi untuk gestureRecognizer(_:shouldReceive:). Dengan kode ini, ketukan pada subview ViewControllertampilan (termasuk imageView) tidak akan memicu printHello(_:)metode.

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

}

Implementasi alternatif gestureRecognizer(_:shouldReceive:)dapat berupa:

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

Namun perlu diperhatikan bahwa kode alternatif ini tidak memeriksa apakah touch.viewmerupakan subview dari gestureRecognizer.view.

Imanou Petit
sumber
10

Solusi cepat lengkap (delegasi harus diimplementasikan DAN ditetapkan untuk pengenal):

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
sumber
6

Varian menggunakan CGPoint yang Anda sentuh (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
sumber
4

Hapus cara Swift

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}
Musa almatri
sumber
Jangan pernah dipanggil di iOS 13, setidaknya dengan swipeGestureRecognizer.
Chewie The Chorkie
Bagaimana ini berbeda dengan jawaban Ian?
sweetfa
3

Perhatikan bahwa GestureRecognizer API telah berubah menjadi:

gestureRecognizer (_: shouldReceive :)

Perhatikan secara khusus indikator garis bawah (lewati) untuk label eksternal parameter pertama.

Menggunakan banyak contoh yang diberikan di atas, saya tidak menerima acara tersebut. Di bawah ini adalah contoh yang berfungsi untuk versi Swift (3+) saat ini.

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
sumber
2

Ditambah solusi di atas, jangan lupa untuk memeriksa User Interaction Enabledsub-tampilan Anda.

masukkan deskripsi gambar di sini

MrDEV
sumber
2

Saya harus mencegah gerakan pada pandangan anak. Satu-satunya hal yang berhasil adalah mengizinkan dan mempertahankan tampilan pertama dan mencegah isyarat di semua tampilan berikutnya:

   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
sumber
1

Cepat 4:

touch.view sekarang opsional, jadi berdasarkan jawaban @ Antoine:

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

Jika Anda tidak ingin 'double-tap recogniser' Anda bertentangan dengan tombol dan / atau kontrol lain, Anda dapat mengatur selfsebagai UIGestureRecognizerDelegatedan menerapkan:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
PDK
sumber