Apakah mungkin bagi UIButton (atau kontrol lainnya dalam hal ini) untuk menerima kejadian sentuh ketika bingkai UIButton berada di luar bingkai induknya? Penyebab ketika saya mencoba ini, UIButton saya sepertinya tidak dapat menerima kejadian apa pun. Bagaimana cara mengatasi ini?
94
Jawaban:
Iya. Anda dapat mengganti
hitTest:withEvent:
metode untuk mengembalikan tampilan untuk sekumpulan poin yang lebih besar dari isi tampilan tersebut. Lihat Referensi Kelas UIView .Edit: Contoh:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGFloat radius = 100.0; CGRect frame = CGRectMake(-radius, -radius, self.frame.size.width + radius, self.frame.size.height + radius); if (CGRectContainsPoint(frame, point)) { return self; } return nil; }
Edit 2: (Setelah klarifikasi :) Untuk memastikan bahwa tombol diperlakukan sebagai dalam batas induk, Anda perlu menimpa
pointInside:withEvent:
induk untuk menyertakan bingkai tombol.- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (CGRectContainsPoint(self.view.bounds, point) || CGRectContainsPoint(button.view.frame, point)) { return YES; } return NO; }
Perhatikan bahwa kode di sana untuk menimpa pointInside tidak sepenuhnya benar. Seperti yang dijelaskan oleh Summon di bawah, lakukan ini:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if ( CGRectContainsPoint(self.oversizeButton.frame, point) ) return YES; return [super pointInside:point withEvent:event]; }
Perhatikan bahwa Anda kemungkinan besar akan melakukannya
self.oversizeButton
sebagai IBOutlet di subclass UIView ini; lalu Anda cukup menyeret "tombol besar" yang dimaksud, ke, tampilan khusus yang dimaksud. (Atau, jika karena alasan tertentu Anda sering melakukan ini dalam sebuah proyek, Anda akan memiliki subkelas UIButton khusus, dan Anda dapat melihat daftar subview Anda untuk kelas-kelas itu.) Semoga membantu.sumber
pointInside:withEvent:
kerangka induk untuk memasukkan bingkai tombol. Saya akan menambahkan beberapa kode contoh ke jawaban saya di atas.hitTest:withEvent:
danpointInside:withEvent:
area yang tidak dipanggil untuk saya ketika saya menekan tombol yang berada di luar batas orang tua. Apa yang salah?@jnic, Saya sedang mengerjakan iOS SDK 5.0 dan agar kode Anda berfungsi dengan benar, saya harus melakukan ini:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (CGRectContainsPoint(button.frame, point)) { return YES; } return [super pointInside:point withEvent:event]; }
Tampilan wadah dalam kasus saya adalah UIButton dan semua elemen anak juga merupakan UIButtons yang dapat bergerak di luar batas UIButton induk.
Terbaik
sumber
Dalam tampilan induk, Anda dapat mengganti metode uji klik:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGPoint translatedPoint = [_myButton convertPoint:point fromView:self]; if (CGRectContainsPoint(_myButton.bounds, translatedPoint)) { return [_myButton hitTest:translatedPoint withEvent:event]; } return [super hitTest:point withEvent:event]; }
Dalam hal ini, jika titik tersebut berada dalam batas-batas tombol Anda, Anda meneruskan panggilan ke sana; jika tidak, kembalikan ke implementasi awal.
sumber
Dalam kasus saya, saya memiliki
UICollectionViewCell
subclass yang berisi fileUIButton
. Saya menonaktifkanclipsToBounds
di sel dan tombolnya terlihat di luar batas sel. Namun, tombol tersebut tidak menerima peristiwa sentuh. Saya dapat mendeteksi peristiwa sentuhan pada tombol dengan menggunakan jawaban Jack: https://stackoverflow.com/a/30431157/3344977Ini versi Swift:
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { let translatedPoint = button.convertPoint(point, fromView: self) if (CGRectContainsPoint(button.bounds, translatedPoint)) { print("Your button was pressed") return button.hitTest(translatedPoint, withEvent: event) } return super.hitTest(point, withEvent: event) }
Cepat 4:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let translatedPoint = button.convert(point, from: self) if (button.bounds.contains(translatedPoint)) { print("Your button was pressed") return button.hitTest(translatedPoint, with: event) } return super.hitTest(point, with: event) }
sumber
Mengapa ini terjadi?
Ini karena jika subview Anda berada di luar batas tampilan super, peristiwa sentuh yang sebenarnya terjadi di subview tersebut tidak akan dikirimkan ke subview tersebut. Namun, AKAN dikirim ke superview-nya.
apa yang kamu butuhkan?
Ketika superview Anda menerima event touch yang disebutkan di atas, Anda harus memberitahu UIKit secara eksplisit bahwa subview saya yang akan menerima event touch ini.
Bagaimana dengan kodenya?
Dalam superview Anda, terapkan
func hitTest(_ point: CGPoint, with event: UIEvent?)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if isHidden || alpha == 0 || clipsToBounds { return super.hitTest(point, with: event) } // convert the point into subview's coordinate system let subviewPoint = self.convert(point, to: subview) // if the converted point lies in subview's bound, tell UIKit that subview should be the one that receives this event if !subview.isHidden && subview.bounds.contains(subviewPoint) { return subview } return super.hitTest(point, with: event) }
Gotchya yang menarik: Anda harus pergi ke "superview tertinggi terlalu kecil"
Anda harus "naik" ke tampilan "tertinggi" di mana tampilan masalahnya ada di luar.
Contoh umum:
Katakanlah Anda memiliki layar S, dengan tampilan wadah C. Tampilan pengontrol wadah adalah V. (Ingat V akan duduk di dalam C dan menjadi ukuran yang identik.) V memiliki subview (mungkin tombol) B. B adalah masalahnya tampilan yang sebenarnya di luar V.
Tetapi perhatikan bahwa B juga berada di luar C.
Dalam contoh ini Anda harus menerapkan solusi
override hitTest
pada kenyataannya untuk C, tidak V . Jika Anda menerapkannya ke V - itu tidak melakukan apa-apa.sumber
Cepat:
Di mana targetView adalah tampilan yang Anda inginkan untuk menerima acara (yang mungkin seluruhnya atau sebagian berada di luar bingkai tampilan induknya).
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { let pointForTargetView: CGPoint = targetView.convertPoint(point, fromView: self) if CGRectContainsPoint(targetView.bounds, pointForTargetView) { return closeButton } return super.hitTest(point, withEvent: event) }
sumber
Bagi saya (seperti untuk orang lain), jawabannya bukanlah untuk mengesampingkan
hitTest
metode, melainkanpointInside
metode. Saya harus melakukan ini hanya di dua tempat.Superview Ini adalah pandangan bahwa jika
clipsToBounds
disetel ke true, itu akan membuat seluruh masalah ini hilang. Jadi, inilah kesederhanaan sayaUIView
di Swift 3:class PromiscuousView : UIView { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return true } }
The Subview Jarak tempuh Anda mungkin berbeda, tetapi dalam kasus saya, saya juga harus menimpa
pointInside
metode untuk subview yang telah memutuskan sentuhan itu di luar batas. Milik saya ada di Objective-C, jadi:- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { WEPopoverController *controller = (id) self.delegate; if (self.takeHitsOutside) { return YES; } else { return [super pointInside:point withEvent:event]; } }
Jawaban ini (dan beberapa orang lain pada pertanyaan yang sama) dapat membantu menjernihkan pemahaman Anda.
sumber
swift 4.1 diperbarui
Untuk mendapatkan kejadian sentuh di UIView yang sama Anda hanya perlu menambahkan metode penggantian berikut, ini akan mengidentifikasi tampilan spesifik yang diketuk di UIView yang ditempatkan di luar batas
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let pointforTargetview:CGPoint = self.convert(point, to: btnAngle) if btnAngle.bounds.contains(pointforTargetview) { return self } return super.hitTest(point, with: event) }
sumber