La prueba de impacto de capa solo devuelve la capa cuando se toca la mitad inferior de la capa

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

Pregunta

Tengo una subcapa en una vista con respaldo de capa. El contenido de la subcapa se establece en una referencia de imagen y es un rect de 25x25. Realizo una prueba de impacto en la super capa cuando se invocan los métodos touchesBegan y touchesMoved. De hecho, el método de prueba de impacto devuelve la subcapa si se toca, PERO solo si se toca la mitad inferior de la imagen. Si se toca la mitad superior de la imagen, en su lugar devuelve la super capa.

Sé que el iPhone OS compensa la tendencia de los toques de los usuarios a ser más bajos de lo previsto. Incluso si cambio el tamaño de la subcapa a un tamaño mayor, 50x50, muestra el mismo comportamiento.

¿Algún pensamiento?

¿Fue útil?

Solución 6

Gracias Kevin ...

Terminé simplemente rehaciendo mi código con UIViews. Fue demasiado complicado tratar de averiguar por qué UIView no sabía qué capa estaba siendo tocada. Mi razonamiento detrás de la idea fue que tendré más de 100 elementos en pantalla al mismo tiempo, y pensé que las Capas serían más fáciles para el procesador que las UIViews. Tengo la aplicación lista y funcionando ahora y 100 vistas no le dan ningún problema.

Mi sugerencia a los demás es simplemente atenerse a las pruebas de UIVview cuando sea posible, hace que el código sea mucho más simple

Otros consejos

La documentación para hitTest dice:

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

Entonces necesitas hacer (algo como esto):

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

(Repetir la respuesta de otra publicación para completar)

Parece que la capa no solo recibe toques en los píxeles inferiores. En cambio, parece que el " real " En la capa de pantalla y los contenidos de la capa están definidos por diferentes CGRects. La imagen se muestra en las coordenadas esperadas, mientras que la capa que responde a los toques está desplazada debajo de la imagen. Por debajo, me refiero al origen de la imagen en (200, 200) y el origen de la capa que responde a los toques está en (200, 220).

A continuación, estoy publicando un código de prueba que usé para recrear el problema. Primero mi subclase de vista y luego mi controlador de vista. Cualquier razonamiento detrás de este problema es muy apreciado.

Mi subclase de vista:

#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

La respuesta aceptada no resuelve el problema, pero la respuesta de schwa lo hace.

Tengo una ventana, un controlador de vista y una vista. La vista recibe toques y llama a hitTest: en su capa. Tuve el mismo problema y descubrí que el punto en la vista es correcto, pero en hitTest: la coordenada y del punto es 20px desactivada, que es la altura de la barra de estado.

Al asignar el punto al sistema de coordenadas de la super capa como sugiere schwa, este problema se solucionó. El " misterioso " Superlayer es la capa raíz de la superview. En mi caso, es la capa de ventana (marco (0, 0, 320, 480)).

Si los toques se reconocen solo en la parte inferior, una posibilidad es que otra subvista cubra la mitad superior de esta subvista. ¿Tienes múltiples subvistas o solo una imagen? En caso de que tenga varias subvistas y cualquiera de ellas tenga el color de fondo como clearColor, intente darle un color de fondo sólido para la prueba. De esa manera sabrá si su subvista está cubierta por otra subvista o no.

Espero que ayude.

He simplificado mi código para encontrar el problema. Solo tengo una subcapa en pantalla y no tengo subvistas. El código listado arriba es lo que estoy ejecutando en el simulador. Dado que solo hay 2 capas en la pantalla, la capa de respaldo de la vista del host y la subcapa que he agregado, nada debe interferir con la prueba de impacto.

En caso de que no esté claro, cuando se toca la parte superior de la subcapa, la prueba de golpe devuelve la capa de respaldo. Además, si toco la capa de respaldo en un punto justo debajo de la subcapa, la subcapa es devuelta por la prueba de impacto.

Es difícil de explicar, pero si ejecuta el código, queda claro.

Gracias

Cuando intento su código, obtengo un comportamiento casi correcto, excepto que los toques a menudo no se reconocen en el borde de la subcapa. Estoy un poco confundido en cuanto a lo que está pasando. Es posible que desee intentar hacer algo como cuando registra un toque, arroje una nueva capa diminuta donde ocurrió el toque (como un cuadrado de 5x5 de algún color) y luego retírela nuevamente 3 segundos después. De esa manera, puede rastrear dónde CA cree que se produjo el toque en comparación con dónde está realmente el cursor.

Por cierto, no es necesario crear una imagen de CG para tu contenido azul, puedes configurar el backgroundColor de la capa en [UIColor blueColor] .CGColor .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top