UITapGestureRecognizer merusak UITableView didSelectRowAtIndexPath

207

Saya telah menulis fungsi saya sendiri untuk menggulir bidang teks saat papan ketik muncul. Untuk menghapus keyboard dengan mengetuk menjauh dari bidang teks, saya telah membuat UITapGestureRecognizeryang menangani pengunduran diri responden pertama pada bidang teks saat mengetuk pergi.

Sekarang saya juga telah membuat pelengkapan otomatis untuk bidang teks yang membuat UITableViewtepat di bawah bidang teks dan mengisinya dengan item saat pengguna memasukkan teks.

Namun, ketika memilih salah satu entri dalam tabel yang dilengkapi otomatis, didSelectRowAtIndexPathtidak dipanggil. Alih-alih, tampaknya pengenal gerakan ketuk dipanggil dan baru saja mengundurkan diri dari responden pertama.

Saya menduga ada beberapa cara untuk memberi tahu pengenal gerakan ketuk untuk terus meneruskan pesan ketuk ke bawah UITableView, tapi saya tidak tahu apa itu. Bantuan apapun akan sangat dihargai.

Jason
sumber
Lihat utas ini .
Duncan Babbage

Jawaban:

258

Ok, akhirnya menemukannya setelah beberapa pencarian melalui dokumen pengenal isyarat.

Solusinya adalah mengimplementasikan UIGestureRecognizerDelegatedan menambahkan yang berikut:

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

Itu merawatnya. Semoga ini akan membantu orang lain juga.

Jason
sumber
Anda juga mungkin perlu melewatkan ketukan pada bidang teks.
Steven Kramer
5
Saya lebih beruntung denganreturn ![touch.view isKindOfClass:[UITableViewCell class]];
Josh Paradroid
1
@Josh Ini tidak berfungsi jika Anda mengetuk label di dalam sel. Sebenarnya jawaban Jason bekerja sempurna untuk apa yang saya butuhkan.
William T.
Tapi ini bukan jawaban untuk masalah Anda, bukan? Ini adalah salah satu / atau. Pada dasarnya setiap sentuhan pada tampilan tabel tidak signifikan dengan pengenal isyarat .... apa yang Anda inginkan adalah KEDUA pengenal gesture DAN pemilihan tampilan tabel untuk bekerja
PostCodeism
5
@Durgaprasad Untuk menonaktifkan pengenal isyarat secara eksplisit ketika memilih sel saya menambahkan ![touch.view isEqual: self.tableView] &&sebelum [touch.view isDescendantOfView: self.tableView]untuk mengecualikan tableViewsendiri (karena isDescendantOfView:juga kembali YESjika argumen-view adalah penerima-view itu sendiri). Harapan itu membantu orang lain yang menginginkan hasil yang sama.
anneblue
220

Cara termudah untuk menyelesaikan masalah ini adalah dengan:

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

Ini memungkinkan UIGestureRecognizermengenali ketukan dan juga meneruskan sentuhan ke responden berikutnya. Konsekuensi yang tidak diinginkan dari metode ini adalah jika Anda memiliki UITableViewCelllayar yang mendorong pengontrol tampilan lain. Jika pengguna mengetuk baris untuk mengabaikan keyboard, baik keyboard dan push akan dikenali. Saya ragu ini yang Anda maksudkan, tetapi metode ini memadai untuk banyak situasi.

Selain itu, memperluas jawaban Robert, jika Anda memiliki pointer ke tampilan tabel yang dimaksud, maka Anda dapat langsung membandingkan kelasnya daripada harus mengkonversi ke string dan berharap Apple tidak mengubah nomenklatur:

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

    return //YES/NO

}

Ingat, Anda juga harus menyatakan UIGestureRecognizermemiliki delegasi dengan kode ini di dalamnya.

TMilligan
sumber
4
Terima kasih, setCancelsTouchesInView mengubah segalanya :)
PostCodeism
Mungkin masih ingin menggunakan isDescendantOfViewalih-alih hanya memeriksa apakah classsama, karena Anda mungkin mengetuk subview. Tergantung pada apa yang Anda butuhkan.
shim
71

Set cancelsTouchesInViewpengenal Anda ke false. Kalau tidak, itu "mengkonsumsi" sentuhan untuk dirinya sendiri, dan tidak meneruskannya ke tampilan tabel. Itu sebabnya acara seleksi tidak pernah terjadi.

misalnya dalam swift

let tapOnScreen: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "CheckTheTime")
tapOnScreen.cancelsTouchesInView = false
view.addGestureRecognizer(tapOnScreen)
Abdulrahman Masoud
sumber
1
Terima kasih banyak untuk ini!
Bishan
1
Ini adalah yang terbersih di Swift. Terima kasih
Dx_
bekerja dengan baik di dalam sel dalam tampilan tabel, indah dan sederhana!
Abdoelrhman
@laoyur sangat benar
bLacK hoLE
masih mendapat klik sel alih-alih gerakan tap
famfamfam
42

Dan untuk Swift (berdasarkan jawaban dari @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
}
Nikolaj Nielsen
sumber
Terima kasih banyak, ini menghemat waktu saya.
Bishan
22

Saya mungkin memiliki solusi yang lebih baik untuk menambahkan gerakan tap pada tampilan tabel tetapi memungkinkan pemilihan sel pada saat yang sama:

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

Saya hanya mencari sel di titik layar di mana pengguna mengetuk. Jika tidak ada jalur indeks yang ditemukan maka saya membiarkan gerakan menerima sentuhan jika tidak saya membatalkannya. Bagi saya ini sangat bagus.

bonokite
sumber
1
Solusi ini mengguncang! Sangat berguna untuk membedakan sel dari bidang kosong pada tabel ..
Alessandro Ornano
18

Saya pikir tidak perlu untuk menulis blok kode hanya cukup diatur cancelsTouchesInViewuntuk falseuntuk objek sikap Anda, secara default itu truedan Anda hanya perlu mengaturnya false. Jika Anda menggunakan UITapGestureobjek dalam kode Anda dan juga menggunakan UIScrollView(tableview, collectionview) maka atur properti inifalse

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
SHUBHAYAN KUNDU
sumber
Terima kasih telah mengirimkan jawaban ini
Gustavo Baiocchi Costa
14

Solusi serupa adalah menerapkan gestureRecognizer:shouldReceiveTouch:menggunakan kelas view untuk menentukan tindakan apa yang harus diambil. Pendekatan ini memiliki keuntungan tidak menutupi tap di wilayah yang mengelilingi tabel secara langsung (tampilan area ini masih diturunkan dari instance UITableView, tetapi tidak mewakili sel).

Ini juga memiliki bonus yang berfungsi dengan beberapa tabel pada satu tampilan (tanpa menambahkan kode tambahan).

Peringatan: ada asumsi bahwa Apple tidak akan mengubah nama kelas.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ![NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"];
}
Robert Altman
sumber
1
Untuk menghilangkan string nama kelas, gunakan baris inireturn ![[touch.view.superview class] isSubclassOfClass:[UITableViewCell class]];
Nianliang
7

Untuk Swift 4.2 solusinya adalah mengimplementasikan UIGestureRecognizerDelegatedan menambahkan yang berikut:

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

ketika Anda mengklik pada tampilan tabel, metode delegasi ini mengembalikan false dan didSelectRowAtIndexPathmetode tampilan tabel bekerja dengan benar.

Ashish Kanani
sumber
1
terima kasih sobat untuk memposting jawaban ini, Anda menghemat waktu saya.
Jay Bhalani
4

Saya memiliki situasi yang berbeda di mana saya ingin fungsi gesture sentuh dipanggil hanya ketika pengguna mengetuk di luar tampilan tabel. Jika pengguna mengetuk di dalam tampilan tabel, maka fungsi gerakan sentuh tidak boleh dipanggil. Selain itu, Jika fungsi gerakan sentuh disebut, fungsi sentuh tetap harus melewati tampilan yang disadap alih-alih mengonsumsinya.

Kode yang dihasilkan adalah kombinasi dari jawaban Abdulrahman Masoud, dan jawaban Nikolaj Nielsen.

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

Dan di MyViewControllerkelas, kelas yang memiliki UITableView, di onViewDidLoad(), saya memastikan untuk memanggil addGestureRecognizer():

class MyViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.addGestureRecognizer()
        ...
    }
    ...
}
Ali Mizan
sumber
3

Hanya mengatur cancels touches in viewuntuk falseuntuk setiap gerakan di bawah (table/collection)View:

- Interfacebuilder

Interfacebuilder

- Kode

<#gestureRecognizer#>.cancelsTouchesInView = false
Mojtaba Hosseini
sumber
bagaimana jika saya ingin menonaktifkannya berfungsi tidak hanya memungkinkan didSelectRowAt untuk bekerja dengannya?
Eyad Shokry
1

Meskipun sudah terlambat dan banyak orang menemukan bahwa saran di atas berfungsi dengan baik, saya tidak bisa mendapatkan metode Jason atau TMilligan untuk bekerja.

Saya memiliki TableView statis dengan beberapa sel yang berisi textFields yang menerima input Angka hanya menggunakan Keyboard Angka. Ini ideal untuk saya:

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

Pastikan Anda telah menerapkan ini <UIGestureRecognizerDelegate>dalam file .h Anda.

Baris ini ![touch.view isKindOfClass:[UITableViewCell class]]memeriksa apakah tableViewCell disadap dan mengabaikan keyboard aktif apa pun.

App Dev Guy
sumber
1

Solusi sederhana menggunakan contoh UIControl di UITableViewCell untuk mendapatkan sentuhan. Anda dapat menambahkan tampilan apa pun dengan userInteractionEnables == NO ke UIControl untuk mendapatkan ketukan.

Andrew Romanov
sumber
0

Ini adalah solusi saya, yang menghubungkan shouldReceiveTouch langsung dari pengenal untuk melihat apakah keyboard ditampilkan.

Dalam delegasi pengenal isyarat keran Anda:

#pragma mark - UIGestureRecognizerDelegate

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

    return NO;
}

Dan PFXKeyboardStateListener.h:

@interface PFXKeyboardStateListener : NSObject
{
    BOOL _isVisible;
}

+ (PFXKeyboardStateListener *)sharedInstance;

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

@end

Dan 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

Anda mungkin ingin memperbarui pola tunggal pendengar keyboard, saya belum mendapatkannya. Semoga ini berhasil untuk semua orang dan juga untuk saya. ^^

bgfriend0
sumber
0

Terapkan metode ini untuk mendelegasikan 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;
}
cxa
sumber
0

Dengan cepat Anda dapat menggunakan ini di dalam

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

func CheckTheTime() -> Bool{
    return true
}
Abdulrahman Masoud
sumber
0

Kasus saya berbeda termasuk uisearchbar dan uitableview on self.view. Saya ingin mengabaikan keyboard uisearchbar dengan menyentuh tampilan.

var tapGestureRecognizer:UITapGestureRecognizer?

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

Pada Metode Delegasi UISearchBar:

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

Saat pengguna menyentuh pada self.view:

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

Swift 5, Mei 2020.

Saya memiliki textField dan tableView yang menjadi terlihat ketika saya memasukkan teks.

Keadaan awal Jadi saya ingin 2 acara berbeda ketika saya mengetuk tableViewCell atau yang lainnya.

Keyboard dan tableView sedang ditampilkan

Pertama-tama kita menambahkan tapGestureRecognizer.

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

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

Kemudian kita tambahkan cek berikut ke dalam UIGestureRecognizerDelegate:

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

}

Jika saya ingin menyembunyikan keyboard terlebih dahulu, tableView tetap terlihat dan responsif terhadap ketukan saya.

masukkan deskripsi gambar di sini

Turki-man
sumber
-1

Ini solusi saya berdasarkan jawaban di atas ... Ini berhasil untuk saya ...

//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
}
iOS
sumber
-1

MASALAH: Dalam kasus saya, masalahnya adalah bahwa saya awalnya menempatkan tombol di setiap sel collectionView dan mengatur batasan untuk mengisi sel, sehingga ketika sel diklik akan mengklik tombol, namun fungsi tombol kosong sehingga tidak ada yang tampaknya terjadi.

FIX: Saya memperbaiki ini dengan menghapus tombol dari sel tampilan koleksi.

Marcus Levi
sumber