他の多くのビューの上にオーバーレイされたビューがあります。私はオーバーリーを使用して画面上のいくつかのタッチを検出していますが、それ以外の場合、スクロールビューなど、その下にある他のビューの動作を停止させたくありません。どのようにすればすべてのタッチを転送できますか?このオーバーレイビュー?UIViewのサブカルスです。
他の多くのビューの上にオーバーレイされたビューがあります。私はオーバーリーを使用して画面上のいくつかのタッチを検出していますが、それ以外の場合、スクロールビューなど、その下にある他のビューの動作を停止させたくありません。どのようにすればすべてのタッチを転送できますか?このオーバーレイビュー?UIViewのサブカルスです。
回答:
ユーザーの操作を無効にするだけで十分でした。
Objective-C:
myWebView.userInteractionEnabled = NO;
迅速:
myWebView.isUserInteractionEnabled = false
myWebView.isUserInteractionEnabled = false
オーバーレイビューからその下のビューにタッチを渡すには、UIViewに次のメソッドを実装します。
Objective-C:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"Passing all touches to the next view (if any), in the view stack.");
return NO;
}
スウィフト5:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
これは古いスレッドですが、検索で見つかったので、2cを追加すると思いました。サブビューのあるUIViewをカバーしていて、サブビューの1つにヒットするタッチのみをインターセプトしたいので、PixelCloudStの回答を変更しました
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView* subview in self.subviews ) {
if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
return YES;
}
}
return NO;
}
@fresidue回答の改良版。このUIView
サブクラスは、サブビューの外側にタッチを渡す透明なビューとして使用できます。Objective-Cでの実装:
@interface PassthroughView : UIView
@end
@implementation PassthroughView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *view in self.subviews) {
if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
@end
。
そしてSwiftでは:
class PassthroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return subviews.contains(where: {
!$0.isHidden
&& $0.isUserInteractionEnabled
&& $0.point(inside: self.convert(point, to: $0), with: event)
})
}
}
次に、大きな「ホルダー」パネルがあり、その後ろにテーブルビューがあるとしましょう。「ホルダー」パネルを作成しPassthroughView
ます。これで機能し、テーブルを「ホルダー」を介してスクロールできます。
だが!
「ホルダー」パネルの上には、いくつかのラベルまたはアイコンがあります。もちろん、ユーザーインタラクションがオフになっていることを示す必要があります。
「ホルダー」パネルの上にいくつかのボタンがあります。忘れないでください、もちろんそれらは単に有効になっているユーザー操作をマークする必要があります!
注使用するビュー-やや紛らわしい、「ホルダー」自体ことPassthroughView
には-ユーザーとの対話を有効にマークされている必要がありONを!それがありますON !! (それ以外の場合、PassthroughViewのコードが呼び出されることはありません。)
$0.isUserInteractionEnabled
UIStackViewでも同様の問題がありました(他のビューでもかまいません)。私の構成は次のとおりでした:
これは、背景に配置する必要のあるコンテナがあり、側面にボタンがある古典的なケースです。レイアウトの目的で、UIStackViewにボタンを含めましたが、今では、stackViewの中央(空)の部分がタッチをインターセプトします:-(
私がしたことは、タッチ可能でなければならないsubViewを定義するプロパティを持つUIStackViewのサブクラスを作成することです。サイドボタン(* viewsWithActiveTouch *配列に含まれる)へのタッチはボタンに与えられ、スタックビューでのこれらのビュー以外のタッチはインターセプトされないため、スタックの下にあるものに渡されます見る。
/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {
var viewsWithActiveTouch: [UIView]?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let activeViews = viewsWithActiveTouch {
for view in activeViews {
if CGRectContainsPoint(view.frame, point) {
return view
}
}
}
return nil
}
}
タッチの転送先のビューがサブビュー/スーパービューでない場合は、次のようにUIViewサブクラスでカスタムプロパティを設定できます。
@interface SomeViewSubclass : UIView {
id forwardableTouchee;
}
@property (retain) id forwardableTouchee;
必ず.mで合成してください:
@synthesize forwardableTouchee;
そして、次のようなUIResponderメソッドのいずれかに以下を含めます。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.forwardableTouchee touchesBegan:touches withEvent:event];
}
UIViewをインスタンス化する場合は常に、イベントの転送先となるビューにforwardableToucheeプロパティを設定します。
SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
view.forwardableTouchee = someOtherView;
Swift 5では
class ThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
guard let slideView = subviews.first else {
return false
}
return slideView.hitTest(convert(point, to: slideView), with: event) != nil
}
}
UIStackViewを介してタッチを渡す必要がありました。内部のUIViewは透過的でしたが、UIStackViewはすべてのタッチを消費しました。これは私のために働きました:
class PassThrouStackView: UIStackView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if view == self {
return nil
}
return view
}
}
すべてのArrangedSubviewsは引き続きタッチを受けますが、UIStackView自体へのタッチは下のビュー(私にとってはmapView)を通過しました。
あなたがここでかなりたくさんの答えをしているように見えますが、私が必要とする迅速できれいな人は誰もいません。そこで私はここで@fresidueからの回答を受け取り、それを現在ほとんどの開発者がここで使用したいものであるため、迅速に変換しました。
ボタン付きの透明なツールバーがありますが、ツールバーがユーザーに表示されず、タッチイベントが通過するという問題が解決しました。
述べられているものは私のテストに基づくオプションではないため、isUserInteractionEnabled = false。
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.hitTest(convert(point, to: subview), with: event) != nil {
return true
}
}
return false
}
StackView内にいくつかのラベルがあり、上記のソリューションではあまり成功しませんでしたが、代わりに以下のコードを使用して問題を解決しました:
let item = ResponsiveLabel()
// Configure label
stackView.addArrangedSubview(item)
UIStackViewのサブクラス化:
class PassThrouStackView:UIStackView{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in self.arrangedSubviews {
let convertedPoint = convert(point, to: subview)
let labelPoint = subview.point(inside: convertedPoint, with: event)
if (labelPoint){
return subview
}
}
return nil
}
}
それからあなたは次のようなことをすることができます:
class ResponsiveLabel:UILabel{
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Respond to touch
}
}
このようなものを試してください...
for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];
上記のコードは、たとえばtouchesBeganメソッドで、タッチをビューのすべてのサブビューに渡します。
私がやろうとしている状況は、ネストされたUIStackView内のコントロールを使用してコントロールパネルを構築することでした。一部のコントロールには、UITextFieldの他のコントロールとUIButtonのコントロールがあります。また、コントロールを識別するためのラベルもありました。私がやりたかったのは、大きな「非表示」ボタンをコントロールパネルの後ろに配置して、ユーザーがボタンまたはテキストフィールドの外側の領域をタップした場合に、それをキャッチしてアクションを実行できるようにすることです。フィールドはアクティブでした(resignFirstResponder)。ただし、コントロールパネルのラベルやその他の空白の領域をタップしても、通過しません。上記の議論は、以下の私の答えを出すのに役立ちました。
基本的に、UIStackViewをサブクラス化し、「point(inside:with)」ルーチンを上書きして、タッチが必要なコントロールのタイプを探し、ラベルなどの無視したいものを「無視」しました。また、UIStackViewの内部をチェックして、コントロールパネル構造に再帰できるようにします。
このコードは、本来あるべきものよりもおそらく少し冗長です。しかし、これはデバッグに役立ち、ルーチンが何をしているかをより明確に提供できると期待しています。インターフェイスビルダーで、UIStackViewのクラスをPassThruStackに変更してください。
class PassThruStack: UIStackView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for view in self.subviews {
if !view.isHidden {
let isStack = view is UIStackView
let isButton = view is UIButton
let isText = view is UITextField
if isStack || isButton || isText {
let pointInside = view.point(inside: self.convert(point, to: view), with: event)
if pointInside {
return true
}
}
}
}
return false
}
}
@PixelCloudStvによって示唆されているように、あるビューから別のビューにタッチをスローしたいが、このプロセスをさらに制御する場合-サブクラスUIView
//ヘッダ
@interface TouchView : UIView
@property (assign, nonatomic) CGRect activeRect;
@end
//実装
#import "TouchView.h"
@implementation TouchView
#pragma mark - Ovverride
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL moveTouch = YES;
if (CGRectContainsPoint(self.activeRect, point)) {
moveTouch = NO;
}
return moveTouch;
}
@end
後、interfaceBuilderでViewのクラスをTouchViewに設定し、アクティブな四角形を四角形で設定します。また、他のロジックを変更して実装することもできます。