レイヤーヒットテストは、レイヤーの下半分に触れたときにのみレイヤーを返します
-
03-07-2019 - |
質問
レイヤー付きビューにサブレイヤーがあります。サブレイヤーのコンテンツは画像参照に設定され、25x25の長方形です。
touchesBeganメソッドとtouchesMovedメソッドが呼び出されたときに、スーパーレイヤーでヒットテストを実行します。実際、ヒットテストメソッドは、サブレイヤーがタッチされた場合にサブレイヤーを返しますが、画像の下半分がタッチされた場合のみです。画像の上半分をタッチすると、代わりにスーパーレイヤーが返されます。
私は、iPhone OSがユーザーのタッチの傾向を意図よりも低く補正していることを知っています。サブレイヤーのサイズを50x50に変更しても、同じ動作を示します。
考えはありますか?
解決 6
ケビンに感謝...
UIViewsでコードをやり直しました。 UIViewがどのレイヤーに触れているのかわからなかった理由を理解しようとするだけでは、非常に面倒でした。アイデアの背後にある私の全体的な理由は、一度に100以上のアイテムを画面に表示し、UIViewsよりもプロセッサー上のLayersの方が簡単だと思っていたことです。現在、アプリを起動して実行していますが、100回のビューで問題は発生していません。
他の人への私の提案は、可能であればUIViewのヒットテストに固執することです。これにより、コードが非常に簡単になります
他のヒント
hitTestのドキュメントによると:
/* Returns the farthest descendant of the layer containing point 'p'.
* Siblings are searched in top-to-bottom order. 'p' is in the
* coordinate system of the receiver's superlayer. */
だからあなたはする必要があります(このような何か):
CGPoint thePoint = [touch locationInView:self];
thePoint = [self.layer convertPoint:thePoint toLayer:self.layer.superlayer];
CALayer *theLayer = [self.layer hitTest:thePoint];
(完全を期すために他の投稿からの回答を繰り返す)
レイヤーは、下のピクセルのタッチだけではないようです。代わりに、「実際の」画面上のレイヤーとレイヤーのコンテンツは、異なるCGRectによって定義されます。画像は予想される座標で表示されますが、タッチに応答するレイヤーは画像の下にオフセットされます。以下では、画像の原点が(200、200)にあり、タッチに応答するレイヤーの原点が(200、220)にあることを意味します。
以下では、問題を再現するために使用したテストコードを投稿しています。最初にビューサブクラス、次にビューコントローラー。この問題の背後にある理由は大歓迎です。
マイビューサブクラス:
#import "myView.h"
#import <QuartzCore/QuartzCore.h>
@implementation myView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint currentLocation = [[touches anyObject] locationInView:self];
CALayer *currentLayer = [self.layer hitTest:currentLocation];
CGPoint center = [currentLayer position];
NSLog([currentLayer valueForKey:@"isRootLayer"]);
if(![currentLayer valueForKey:@"isRootLayer"]) {
if (center.x != 200) {
[currentLayer setPosition:CGPointMake(200.0f, 200.0f)];
} else {
[currentLayer setPosition:CGPointMake(100.0f, 100.0f)];
}
}
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
[super dealloc];
}
@end
My View Controller:
#import "layerTestViewController.h"
#import <QuartzCore/QuartzCore.h>
#define ELEMENT_PERIOD_SIZE 50
#define ELEMENT_GROUP_SIZE 50
@implementation layerTestViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *myLayer = [CALayer layer];
CGRect layerFrame =CGRectMake(0.0f, 0.0f, ELEMENT_GROUP_SIZE, ELEMENT_PERIOD_SIZE);
myLayer.frame = layerFrame;
[myLayer setName:[NSString stringWithString:@"test"]];
[myLayer setValue:[NSString stringWithString:@"testkey"] forKey:@"key"];
UIGraphicsBeginImageContext(layerFrame.size);
[[UIColor blueColor] set];
UIRectFill(layerFrame);
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myLayer.contents = (id)[theImage CGImage];
myLayer.position = CGPointMake(100.0f, 100.0f);
[self.view.layer addSublayer:myLayer];
[self.view.layer setValue:[NSString stringWithString:@"YES"] forKey:@"isRootLayer"];
NSLog([self.view.layer valueForKey:@"isRootLayer"]);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[super dealloc];
}
@end
受け入れられた答えは問題を解決しませんが、schwaによる答えは解決します。
ウィンドウ、View Controller、Viewがあります。ビューはタッチを受信し、そのレイヤーで hitTest:
を呼び出します。同じ問題が発生し、ビュー内のポイントが正しいことがわかりましたが、 hitTest:
では、ポイントのy座標が20pxオフになっています。これがステータスバーの高さです。
シュワが示唆するように、ポイントをスーパーレイヤーの座標系にマッピングすることにより、この問題は修正されました。 「神秘的な」スーパーレイヤーは、スーパービューのルートレイヤーです。私の場合は、ウィンドウレイヤー(フレーム(0、0、320、480))です。
タッチが下部でのみ認識されている場合、可能性の1つは、別のサブビューがこのサブビューの上半分を覆っていることです。複数のサブビューがありますか、それとも画像が1つだけですか?複数のサブビューがあり、それらのいずれかがclearColorとして背景色を持っている場合は、テストのために単色の背景色を指定してみてください。これにより、サブビューが別のサブビューで覆われているかどうかがわかります。
役立つこと。
問題を見つけるためにコードを単純化しました。画面にはサブレイヤーが1つしかなく、サブビューはありません。上記のコードは、シミュレータで実行しているものです。画面には2つのレイヤー(ホストビューのバッキングレイヤーと追加したサブレイヤー)しかないため、ヒットテストを妨げるものは何もありません。
明確でない場合、サブレイヤーの上部に触れると、ヒットテストは代わりにバッキングレイヤーを返します。また、サブレイヤーのすぐ下のバッキングレイヤーに触れると、ヒットテストによってサブレイヤーが返されます。
説明するのは難しいですが、コードを実行すると明らかになります。
ありがとう
コードを試すと、サブレイヤーの端でタッチが認識されないことが多いことを除いて、ほぼ正しい動作が得られます。私は何が起こっているのか少し混乱しています。タッチを登録するときのように、タッチが発生した場所に新しい小さなレイヤー(ある色の5x5の正方形など)を投げてから、3秒後に再び削除することができます。このようにして、タッチが発生したとCAが考えている場所とカーソルが実際にある場所を追跡できます。
ところで、青のコンテンツ用にCGImageを作成する必要はありません。レイヤーの backgroundColor
を [UIColor blueColor] .CGColor
に設定するだけです。