UIButtonのフレームがその親のフレームの外側にある場合、UIButton(またはそのほかのコントロール)がタッチイベントを受け取ることは可能ですか?これを試すと、UIButtonがイベントを受信できないようです。これを回避するにはどうすればよいですか?
UIButtonのフレームがその親のフレームの外側にある場合、UIButton(またはそのほかのコントロール)がタッチイベントを受け取ることは可能ですか?これを試すと、UIButtonがイベントを受信できないようです。これを回避するにはどうすればよいですか?
回答:
はい。hitTest:withEvent:
メソッドをオーバーライドして、ビューに含まれているよりも大きなポイントセットのビューを返すことができます。UIViewクラスリファレンスを参照してください。
編集:例:
- (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;
}
編集2:(説明後:)ボタンが親の境界内にあるものとして扱われるようにpointInside:withEvent:
するには、ボタンのフレームを含めるように親でオーバーライドする必要があります。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(self.view.bounds, point) ||
CGRectContainsPoint(button.view.frame, point))
{
return YES;
}
return NO;
}
pointInsideをオーバーライドするためにそこにあるコードはまったく正しくないことに注意してください。Summonが以下で説明するように、これを行います:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if ( CGRectContainsPoint(self.oversizeButton.frame, point) )
return YES;
return [super pointInside:point withEvent:event];
}
self.oversizeButton
このUIViewサブクラスのIBOutletとして使用する可能性が非常に高いことに注意してください。次に、問題の「特大ボタン」を問題の特別なビューにドラッグするだけです。(または、何らかの理由でプロジェクトでこれを頻繁に行っている場合は、特別なUIButtonサブクラスがあり、それらのクラスのサブビューリストを調べることができます。)お役に立てば幸いです。
pointInside:withEvent:
ボタンのフレームを含めるには、親をオーバーライドする必要があります。上記の回答にサンプルコードを追加します。
hitTest:withEvent:
pointInside:withEvent:
親の境界外にあるボタンを押しても、エリアは呼び出されません。何が悪いのでしょうか?
@jnic、私はiOS SDK 5.0に取り組んでおり、あなたのコードを正しく機能させるために私はこれをしなければなりませんでした:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(button.frame, point)) {
return YES;
}
return [super pointInside:point withEvent:event]; }
私の場合のコンテナービューはUIButtonであり、すべての子要素も親UIButtonの境界の外に移動できるUIButtonです。
ベスト
親ビューでは、ヒットテストメソッドをオーバーライドできます。
- (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];
}
この場合、ポイントがボタンの範囲内にある場合は、そこに通話を転送します。そうでない場合は、元の実装に戻します。
私の場合、を含むUICollectionViewCell
サブクラスがありましたUIButton
。clipsToBounds
セルで無効にすると、ボタンがセルの境界の外に表示されました。ただし、ボタンはタッチイベントを受信していませんでした。ジャックの答えを使用して、ボタンのタッチイベントを検出できました:https : //stackoverflow.com/a/30431157/3344977
これが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)
}
スウィフト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)
}
なぜこれが起こっているのですか?
これは、サブビューがスーパービューの境界の外にある場合、そのサブビューで実際に発生するタッチイベントがそのサブビューに配信されないためです。しかし、それはWILLそのスーパーに配信されます。
サブビューが視覚的にクリップされるかどうかに関係なく、タッチイベントは常にターゲットビューのスーパービューの境界の長方形を尊重します。つまり、スーパービューの境界の長方形の外側にあるビューの一部で発生するタッチイベントは、そのビューに配信されません。リンク
するべきこと?
スーパービューが上記のタッチイベントを受け取ったら、このサブイベントがこのタッチイベントを受け取るものであることをUIKitに明示的に通知する必要があります。
コードはどうですか?
スーパービューで、実装します 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)
}
魅力的なゴッチャ:「最高の小さすぎるスーパービュー」に行く必要があります
問題のビューが外側にある「最高」のビューに「上」に移動する必要があります。
典型的な例:
コンテナービューCの画面Sがあるとします。コンテナービューコントローラーのビューはVです(VはCの内側に配置され、同じサイズになります)。実際にはVの外側にあるビュー。
ただし、BもCの外側にあることに注意してください。
この例では、解決策を適用する必要がありoverride hitTest
、実際にはCに、ではないVへ。Vに適用すると、何も起こりません。
迅速:
ここで、targetViewは、イベントを受信するビューです(全体または一部が親ビューのフレームの外側にある場合があります)。
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)
}
(他の人のためのように)私にとっては、答えはしないで上書きするhitTest
方法ではなく、pointInside
方法を。これを2か所で行う必要がありました。
Superview
これは、clipsToBounds
trueに設定した場合、この問題全体が消えるという見方です。これUIView
がSwift 3 の私のシンプルなコードです。
class PromiscuousView : UIView {
override func point(inside point: CGPoint,
with event: UIEvent?) -> Bool {
return true
}
}
サブビュー
マイレージは異なる場合がありますが、私の場合pointInside
、タッチが範囲外であると判断したサブビューのメソッドも上書きする必要がありました。鉱山はObjective-Cなので、次のようになります。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
WEPopoverController *controller = (id) self.delegate;
if (self.takeHitsOutside) {
return YES;
} else {
return [super pointInside:point withEvent:event];
}
}
この回答(および同じ質問に対する他のいくつかの回答)は、理解を深めるのに役立ちます。
swift 4.1が更新されました
同じUIViewでタッチイベントを取得するには、次のオーバーライドメソッドを追加するだけで、境界外に配置されたUIViewでタップされた特定のビューを識別します
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)
}