UIButton(または他のコントロール)は、UIButtonのフレームが親のフレームの外側にあるときにタッチイベントを受け取ることは可能ですか?これを試すと、UIButtonがイベントを受信できないようです。これを回避するにはどうすればよいですか?
はい。 hitTest:withEvent:
メソッドをオーバーライドして、そのビューに含まれるよりも大きなポイントセットのビューを返すことができます。 IView Class Reference を参照してください。
編集:例:
- (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];
}
このUIViewサブクラスでIBOutletとしてself.oversizeButton
を使用する可能性が非常に高いことに注意してください。次に、問題の「特大ボタン」を問題の特別なビューにドラッグします。 (または、何らかの理由でプロジェクトでこれを頻繁に行っている場合は、特別なUIButtonサブクラスがあり、それらのクラスのサブビューリストを調べることができます。)役に立てば幸いです。
@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)
}
迅速:
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)
}
なぜこれが起こっているのですか?
これは、サブビューがスーパービューの境界外にある場合、そのサブビューで実際に発生するタッチイベントがそのサブビューに配信されないためです。ただし、[〜#〜] [〜#〜]はスーパービューに配信されます。
サブビューが視覚的にクリップされるかどうかに関係なく、タッチイベントは常にターゲットビューのスーパービューの境界矩形を尊重します。つまり、スーパービューの境界矩形の外側にあるビューの一部で発生したタッチイベントは、そのビューに配信されません。 リンク
何をする必要がありますか?
スーパービューが上記のタッチイベントを受け取ると、サブビューがこのタッチイベントを受け取るサブビューであることを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があります。実際にVの外側にあるビュー.
ただし、BはCの外側にもあることに注意してください。
この例では、ソリューションを適用する必要がありますoverride hitTest
実際にはVではなくCに。 Vに適用すると、何もしません。
私(他の人)の場合、答えはhitTest
メソッドをオーバーライドするnotではなく、pointInside
でした方法。私はこれをたった2か所でしなければなりませんでした。
Superviewこれは、clipsToBounds
がtrueに設定されている場合、この問題全体が消えるというビューです。 Swift 3:のシンプルなUIView
in
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)
}