UIButton di dalam tampilan yang memiliki UITapGestureRecognizer


184

Saya memiliki pandangan dengan UITapGestureRecognizer. Jadi ketika saya mengetuk tampilan, tampilan lain muncul di atas tampilan ini. Tampilan baru ini memiliki tiga tombol. Ketika saya sekarang menekan salah satu tombol ini saya tidak mendapatkan tindakan tombol, saya hanya mendapatkan tindakan gerakan tap. Jadi saya tidak dapat menggunakan tombol-tombol ini lagi. Apa yang bisa saya lakukan untuk mendapatkan acara melalui tombol-tombol ini? Yang aneh adalah bahwa tombol masih disorot.

Saya tidak bisa menghapus UITapGestureRecognizer setelah saya menerima ketuknya. Karena dengan itu tampilan baru juga dapat dihapus. Berarti saya ingin perilaku seperti kontrol vide layar penuh .

Jawaban:


256

Anda dapat mengatur pengontrol atau tampilan Anda (mana yang membuat pengenal gesture) sebagai delegasi dari UITapGestureRecognizer. Kemudian dalam delegasi Anda dapat menerapkan -gestureRecognizer:shouldReceiveTouch:. Dalam implementasi Anda, Anda dapat menguji apakah sentuhan itu milik subview baru Anda, dan jika ya, instruksikan pengenal gesture untuk mengabaikannya. Sesuatu seperti yang berikut ini:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // test if our control subview is on-screen
    if (self.controlSubview.superview != nil) {
        if ([touch.view isDescendantOfView:self.controlSubview]) {
            // we touched our control surface
            return NO; // ignore the touch
        }
    }
    return YES; // handle the touch
}

Dalam file header saya, saya memiliki pandangan saya mengimplementasikan UIGestoreRegognizerDelegate, dan dalam .mi saya menambahkan kode Anda di atas. Keran tidak pernah memasuki metode ini, langsung ke handler saya. Ada ide?
kmehta

1
@kmehta Anda kemungkinan besar lupa untuk mengatur properti delegasi UIGestureRecognizer.
Hingga

Bisakah jawaban ini digeneralisasikan untuk menangani semua kasus di mana ada IBAction yang terlibat?
Martin Wickman

@Martin IBAction mengkompilasi ke bawah voidsehingga tidak meninggalkan informasi apa pun saat runtime untuk digunakan untuk deteksi. Tetapi yang dapat Anda lakukan adalah menaiki hierarki tampilan, menguji UIControl, dan kembali NOjika Anda menemukan kontrol.
Lily Ballard

Terima kasih untuk ini, ini membantu saya. jika tombol Anda dalam UIImageView, pastikan jika userInteractionEnabled dari imageView diatur pada YES.
james075

156

Sebagai tindak lanjut dari tindak lanjut Casey untuk jawaban Kevin Ballard:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
    return YES; // handle the touch
}

Ini pada dasarnya membuat semua jenis input pengguna kontrol seperti tombol, slider, dll berfungsi


1
Itu solusi fleksibel yang dapat disertai dengan setCancelsTouchesInView = NOuntuk tidak memicu gerakan tap pada interaksi dengan kontrol. Namun Anda dapat menulisnya dengan lebih baik:return ![touch.view isKindOfClass:[UIControl class]];
griga13

Solusi Swift 4+: return! (Touch.view adalah UIControl)
nteissler

103

Temukan jawaban ini di sini: tautan

Anda juga bisa menggunakan

tapRecognizer.cancelsTouchesInView = NO;

Yang mencegah pengenal ketuk menjadi satu-satunya yang menangkap semua ketukan

UPDATE - Michael menyebutkan tautan ke dokumentasi yang menggambarkan properti ini: cancelsTouchesInView


8
Ini harus menjadi jawaban yang diterima IMO. Ini memungkinkan setiap subview menangani sendiri tap dan mencegah tampilan yang memiliki tabrecognizer yang melekat padanya dari 'highjacking' keran. Tidak perlu menerapkan delegasi jika semua yang Anda ingin lakukan adalah membuat tampilan dapat diklik (untuk mengundurkan diri responden pertama / sembunyikan keyboard di textfields dll) .. hebat!
EeKay

4
Ini seharusnya menjadi jawaban yang diterima. Jawaban ini membuat saya membaca dokumentasi Apple dengan benar, yang membuatnya jelas bahwa pengenal gesture akan mencegah subview dari mendapatkan peristiwa yang dikenali kecuali Anda melakukan ini.
Michael van der Westhuizen

1
Ini digunakan untuk bekerja tetapi sekarang tidak berfungsi untuk saya .. mungkin karena saya memiliki scrollView di bawah tombol? Saya harus mengimplementasikan delegasi seperti pada jawaban di atas.
xissburg

7
Setelah bereksperimen sedikit, saya perhatikan bahwa ini hanya akan berfungsi jika tampilan yang berisi pengenal gesture adalah saudara kandung dari UIControl, dan itu tidak akan terjadi jika view adalah induk dari UIControl.
xissburg

6
Tidak benar-benar berfungsi sebagaimana mestinya ... YA, itu mencegah pengenalan keran untuk memakan acara di UIControl. Tapi masih menjalankan tindakan pengenal, yang menjalankan 2 tindakan pada saat yang sama.
Tek Yin

71

Sebagai tindak lanjut dari jawaban Kevin Ballard, saya memiliki masalah yang sama dan akhirnya menggunakan kode ini:

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

Ini memiliki efek yang sama tetapi ini akan bekerja pada setiap UIButton pada kedalaman tampilan apa pun (UIButton saya memiliki beberapa pandangan yang dalam dan delegasi UIGestureRecognizer tidak memiliki referensi untuk itu.)


6
Tidak ingin menjadi kontol di sini, tapi itu benar-benar YA dan TIDAK. Silakan gunakan konvensi Kakao. TRUE / FALSE / NULL adalah untuk CoreFoundation-Layer.
steipete

dari apa yang saya baca, YA dan TIDAK sebenarnya diciptakan untuk keterbacaan. keterbacaan bersifat subyektif. itu berasal dari metode dan konvensi penamaan properti yang tidak semua orang terlalu peduli. jika Anda ingin kepatuhan ketat pada konvensi penamaan maka ya gunakan YA dan TIDAK untuk NS apa pun. namun, saya akan mengatakan bahwa jika Anda mensurvei pengembang, Anda AKAN mendapatkan pendapat beragam tentang yang mana yang lebih mudah dibaca [tombol setHidden: YES]; -atau- [tombol setHidden: BENAR];
Pimp Juice McJones

1
Saya kira yang ingin saya katakan adalah bahwa jika premis dari konvensi penamaan mudah dibaca maka saya pikir sulit untuk menyangkal bahwa itu subjektif dalam beberapa kasus. jika Anda seorang yang murni maka Anda tidak akan mengerti pernyataan itu.
Pimp Juice McJones

2
Ini mungkin tampak subjektif tetapi setelah beberapa saat pemrograman Kakao Anda benar-benar masuk ke dalam aliran kode yang secara semantik lebih akurat ... "BENAR" tampaknya benar-benar salah sekarang setelah bertahun-tahun Objective-C, karena tidak cocok dengan "tersembunyi" sangat baik.
Kendall Helmstetter Gelner

Sejak kapan bisa / apakah Anda menyampaikan gerakan ketukan ke UIButton sebagai Acara Touch Up Inside UITouch (yang terakhir adalah acara yang dihasilkan dengan mengetuk UIButton)? Tampaknya duplikat dan salah untuk membuat pengenal gerakan ketuk untuk tombol yang memiliki 15 aktivitas sentuh yang telah dikaitkan dengan gerakan tap. Saya ingin melihat kode, bukan tebakan.
James Bush

10

Di iOS 6.0 dan yang lebih baru, tindakan kontrol default mencegah perilaku pengenalan isyarat yang tumpang tindih. Misalnya, tindakan default untuk sebuah tombol adalah satu ketukan. Jika Anda memiliki pengenal gerakan ketukan tunggal yang terpasang pada tampilan induk tombol, dan pengguna mengetuk tombol, maka metode tindakan tombol menerima acara sentuh alih-alih pengenal gerakan. Ini hanya berlaku untuk pengenalan gerakan yang tumpang tindih dengan tindakan default untuk kontrol, yang meliputi: .....

Dari dokumen API Apple


8

Jawaban-jawaban ini tidak lengkap. Saya harus membaca banyak posting tentang cara menggunakan operasi boolean ini.

Dalam file * .h Anda, tambahkan ini

@interface v1ViewController : UIViewController <UIGestureRecognizerDelegate>

Dalam file * .m Anda, tambahkan ini

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    NSLog(@"went here ...");

    if ([touch.view isKindOfClass:[UIControl class]])
    {
        // we touched a button, slider, or other UIControl
        return NO; // ignore the touch
    }
    return YES; // handle the touch
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.



    //tap gestrure
    UITapGestureRecognizer *tapGestRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(screenTappedOnce)];
    [tapGestRecog setNumberOfTapsRequired:1];
    [self.view addGestureRecognizer:tapGestRecog];


// This line is very important. if You don't add it then your boolean operation will never get called
tapGestRecog.delegate = self;

}


-(IBAction) screenTappedOnce
{
    NSLog(@"screenTappedOnce ...");

}

7

Temukan cara lain untuk melakukannya dari sini . Ini mendeteksi sentuhan apakah di dalam setiap tombol atau tidak.

(1) pointInside: withEvent: (2) locationInView:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch {
    // Don't recognize taps in the buttons
    return (![self.button1 pointInside:[touch locationInView:self.button1] withEvent:nil] &&
            ![self.button2 pointInside:[touch locationInView:self.button2] withEvent:nil] &&
            ![self.button3 pointInside:[touch locationInView:self.button3] withEvent:nil]);
}

Saya mencoba semua solusi lain untuk ini, tetapi pendekatan -pointInside: withEvent: adalah satu-satunya yang bekerja untuk saya.
Kenny Wyland

3

Inilah versi Swift dari jawaban Lily Ballard yang cocok untuk saya:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if (scrollView.superview != nil) {
        if ((touch.view?.isDescendantOfView(scrollView)) != nil) { return false }
    }
    return true
}

3

Cepat 5

Tombol superview dengan tapgesture

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let _ = touch.view as? UIButton { return false }
    return true
}

Dalam kasus saya menerapkan hitTest bekerja untuk saya. Saya memiliki tampilan koleksi dengan tombol

Metode ini melintasi hierarki tampilan dengan memanggil point(inside:with:)metode setiap subview untuk menentukan subview mana yang harus menerima acara sentuh. Jika point(inside:with:)mengembalikan true, maka hierarki subview ini juga ditelusuri hingga tampilan paling depan yang berisi titik yang ditentukan ditemukan.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard isUserInteractionEnabled else { return nil }

    guard !isHidden else { return nil }

    guard alpha >= 0.01 else { return nil }

    guard self.point(inside: point, with: event) else { return nil }

    for eachImageCell in collectionView.visibleCells {
        for eachImageButton in eachImageCell.subviews {
            if let crossButton = eachImageButton as? UIButton {
                if crossButton.point(inside: convert(point, to: crossButton), with: event) {
                    return crossButton
                }
            }
        }
    }
    return super.hitTest(point, with: event)
}

1

Anda dapat menghentikan UITapGestureRecognizer dari membatalkan acara lain (seperti ketukan pada tombol Anda) dengan mengatur boolean berikut:

    [tapRecognizer setCancelsTouchesInView:NO];

1

Jika skenario Anda seperti ini:

Anda memiliki tampilan sederhana dan beberapa UIButtons, kontrol UITextField ditambahkan sebagai subview ke tampilan itu. Sekarang Anda ingin mengabaikan keyboard ketika Anda menyentuh tempat lain pada tampilan kecuali pada kontrol (subview yang Anda tambahkan)

Maka Solusi adalah:

Tambahkan metode berikut ke XYZViewController.m Anda (yang memiliki pandangan Anda)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

Pertama saya mencoba dengan jawaban @cdasher tetapi dalam kasus saya bahkan pada mengklik tombol, saya mendapatkan touch.view sebagai "tampilan" saya di ViewController tetapi bukan kontrol "UIButton" saya. Adakah yang bisa mengatakan mengapa properti tampilan sentuhan tidak mengembalikan tampilan asli tempat keran terjadi
NaveenRaghuveer

1
Tidak akan berfungsi jika Anda memiliki tayangan gulir atau tampilan lain yang mengklaim acara sentuh.
lkraider

1

Mengoptimalkan jawaban cdasher, Anda dapatkan

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch 
{
    return ![touch.view isKindOfClass:[UIControl class]];
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.