Le test de calque ne renvoie le calque que lorsque la moitié inférieure du calque est touchée

StackOverflow https://stackoverflow.com/questions/401040

Question

J'ai une sous-couche sur une vue sauvegardée sur des couches. Le contenu de la sous-couche est défini sur une référence d'image et correspond à un rect 25x25.
J'effectue un test de hit sur la super-couche lorsque les méthodes touchesBegan et touchesMoved sont invoquées. En fait, la méthode de test de rendu renvoie la sous-couche si elle est touchée, MAIS uniquement si la moitié inférieure de l'image est touchée. Si vous touchez la moitié supérieure de l’image, elle renvoie la super-couche.

Je sais que l'iPhone OS compense la tendance des contacts de l'utilisateur à être inférieure à celle prévue. Même si je redimensionne la sous-couche à une taille supérieure, 50x50, elle présente le même comportement.

Des pensées?

Était-ce utile?

La solution 6

Merci Kevin ...

J'ai fini par refaire mon code avec UIViews. C'était trop compliqué d'essayer de comprendre pourquoi UIView ne savait pas quelle couche était touchée. Tout mon raisonnement derrière l'idée était que j'aurais plus de 100 articles à l'écran en même temps, et je pensais que les calques seraient plus faciles pour le processeur que pour UIViews. L'application est opérationnelle et 100 vues ne lui causent aucun problème.

Ma suggestion aux autres est simplement de s'en tenir à des tests de hit UIViews lorsque cela est possible, cela simplifie beaucoup le code

Autres conseils

La documentation de hitTest indique:

/* 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. */

Donc, vous devez faire (quelque chose comme ceci):

CGPoint thePoint = [touch locationInView:self];
thePoint = [self.layer convertPoint:thePoint toLayer:self.layer.superlayer];
CALayer *theLayer = [self.layer hitTest:thePoint];

(Répétition de la réponse d'un autre message par souci d'exhaustivité)

Il semble que le calque ne reçoive pas que des touches sur les pixels du bas. Au lieu de cela, il semble que le " réel " sur la couche d'écran et le contenu de la couche sont définis par différents CGRects. L'image est affichée aux coordonnées attendues, tandis que le calque qui répond au toucher est décalé sous l'image. Par ci-dessous, je veux dire que l’origine de l’image est à (200, 200) et celle du calque qui répond au toucher est à (200, 220).

Ci-dessous, je publie un code de test que j'ai utilisé pour recréer le problème. D'abord ma sous-classe de vue, puis mon contrôleur de vue. Tout raisonnement derrière ce problème est grandement apprécié.

Sous-classe My View:

#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

Mon contrôleur de vue:

#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

La réponse acceptée ne résout pas le problème, mais la réponse de schwa le fait.

J'ai une fenêtre, un contrôleur de vue et une vue. La vue reçoit des touches et appelle hitTest: sur son calque. J'ai eu le même problème et j'ai découvert que le point de la vue était correct, mais dans hitTest: , la coordonnée y du point est 20px off, ce qui se trouve être à la hauteur de la barre d'état.

En mappant le point sur le système de coordonnées de la super-couche, comme suggéré par schwa, ce problème est résolu. Le "mystérieux" superlayer est la couche racine du superview. Dans mon cas, c’est la couche fenêtre (frame (0, 0, 320, 480)).

Si les contacts sont reconnus sur la partie inférieure uniquement, il est possible qu'une autre sous-vue couvre la moitié supérieure de cette sous-vue. Avez-vous plusieurs sous-vues ou une seule image? Si vous avez plusieurs sous-vues et que l'une d'entre elles a la couleur de fond clearColor, essayez de lui donner une couleur de fond unie à des fins de test. Ainsi, vous saurez si votre sous-vue est couverte ou non par une autre.

L’espoir que cela aide.

J'ai simplifié mon code pour trouver le problème. Je n'ai qu'une sous-couche à l'écran et pas de sous-vues. Le code répertorié ci-dessus est ce que je cours dans le simulateur. Puisqu'il n'y a que 2 couches à l'écran, la couche de sauvegarde de la vue de l'hôte et la sous-couche que j'ai ajoutée, rien ne doit interférer avec le test de réussite.

Au cas où ce ne serait pas clair, lorsque le haut de la sous-couche est touché, le test de rendu renvoie à la place la couche de support. De plus, si je touche la couche de support juste en dessous de la sous-couche, la sous-couche est renvoyée par le test d’attraction.

C'est difficile à expliquer, mais si vous exécutez le code, cela devient clair.

Merci

Lorsque j'essaie votre code, j'obtiens un comportement presque correct, sauf que les contacts ne sont souvent pas reconnus sur le bord même de la sous-couche. Je suis un peu confus quant à ce qui se passe. Vous voudrez peut-être essayer de faire quelque chose comme lorsque vous enregistrez une touche, jetez un nouveau petit calque là où la touche s'est produite (comme un carré de couleur 5x5), puis supprimez-le à nouveau 3 secondes plus tard. De cette façon, vous pouvez savoir où CA pense avoir touché le téléphone par rapport à l'endroit où se trouve réellement votre curseur.

BTW, vous n'avez pas besoin de créer une CGImage pour votre contenu bleu. Vous pouvez simplement définir backgroundColor du calque sur [UIColor blueColor] .CGColor .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top