2008-12-30 7 views
4

J'ai une sous-couche sur une vue avec couche. Le contenu de la sous-couche est défini sur une image Ref et est un rectangle 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 coup renvoie la sous-couche si elle est touchée, MAIS seulement si la moitié inférieure de l'image est touchée. Si la moitié supérieure de l'image est touchée, elle renvoie le super-calque à la place.test d'atteinte de couche retour d'une couche uniquement lorsque la moitié inférieure de la couche est touchée

Je sais que l'iPhone OS compense la tendance des touches de l'utilisateur à être plus faible que prévu. Même si je redimensionne la sous-couche à une taille plus grande, 50x50, elle présente le même comportement.

Des pensées?

Répondre

0

Merci Kevin ...

J'ai fini juste refaire mon code avec UIViews. C'était trop difficile 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 éléments à l'écran en même temps, et je pensais que Layers serait plus facile sur le processeur que UIViews. J'ai l'application en marche maintenant et 100 vues ne lui donne aucun hoquet.

Ma suggestion à d'autres est bâton juste avec UIViews de test de vie lorsque cela est possible, il rend le code plus simple

1

Il semble que la couche ne reçoit pas seulement des touches sur les pixels inférieurs. Au lieu de cela, il semble que le "réel" à l'écran et le contenu de la couche sont définis par différents CGRects. L'image est affichée aux coordonnées souhaitées, tandis que la couche qui répond aux touches est décalée sous l'image. Par ci-dessous, je veux dire l'origine de l'image est à (200, 200) et l'origine de la couche qui répond aux touches est à (200, 220). Ci-dessous, je poste un code de test que j'ai utilisé pour recréer le problème. D'abord ma sous-classe vue puis mon contrôleur de vue. Tout raisonnement derrière ce problème est grandement apprécié.

Ma Voir sous-classe:

#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 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 
0

Si des touches sont reconnus sur la partie inférieure seulement, il est possible que l'autre sous-vue couvre la moitié supérieure de cette sous-vue Avez-vous plusieurs sous-vues ou une seule image? Dans le cas où vous avez plusieurs sous-vues et que l'une d'entre elles a la couleur d'arrière-plan comme clearColor, essayez de lui donner une couleur de fond solide pour les tests. De cette façon, vous saurez si votre sous-vue est couverte par une autre sous-vue ou non.

Espérons que ça aide.

0

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

Dans le cas où ce n'était pas clair, lorsque la partie supérieure de la sous-couche est touchée, le test de retour renvoie la couche de support à la place. Aussi, si je touche la couche de support à un point juste au-dessous de la sous-couche, la sous-couche est renvoyée par le test d'atteinte.

Il est difficile à expliquer, mais si vous exécutez le code, il devient clair.

Merci

-1

Lorsque je tente votre code, je reçois un comportement presque correct, à l'exception des touches ne sont souvent pas reconnus au bord même de la sous-couche. Je suis un peu confus quant à ce qui se passe. Vous pouvez essayer de faire quelque chose comme lorsque vous enregistrez une touche, lancez une nouvelle couche minuscule là où le contact a eu lieu (comme un carré de 5x5 de couleur) et retirez-le à nouveau 3 secondes plus tard. De cette façon, vous pouvez suivre où CA pense que le contact s'est produit par rapport à l'endroit où votre curseur est réellement.

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

11

La documentation dit 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. */ 

donc vous devez faire (quelque chose comme ça):

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

(réponse d'autres répétant après à cause de l'exhaustivité)

+0

Merci! J'ai eu le même problème, et ça me rendait fou. Je suis encore un peu confus quant à savoir pourquoi c'est nécessaire, mais ça semble fonctionner. –

+0

Je pense qu'un problème est que les coordonnées de la vue CA sont ignorées des coordonnées UIView (en bas à gauche est l'origine)? Mais j'aimerais entendre quelqu'un qui peut élaborer sur la hiérarchie de la vue et de la couche et expliquer vraiment ce qui se passe d'autre. –

+0

Cette réponse m'a sauvé beaucoup de temps et de peine - merci! Il y a encore quelque chose que je ne comprends pas: si 'self' est la vue qui a reçu le toucher, alors' self.layer' est son calque correspondant - alors qu'est-ce que 'self.layer.superlayer'? – wcochran

1

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

J'ai une fenêtre, un contrôleur de vue et une vue. La vue reçoit des touches et appelle hitTest: sur sa couche. J'ai eu le même problème et j'ai découvert que le point dans la vue est 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 au système de coordonnées de la super-couche comme suggéré par schwa, ce problème est résolu. La super-couche "mystérieuse" est la couche racine de la superview. Dans mon cas, c'est la couche de fenêtre (frame (0, 0, 320, 480)).

Questions connexes