2011-03-25 21 views
71

¿Es posible que un UIButton (o cualquier otro control para el caso) reciba eventos táctiles cuando el marco de UIButton se encuentra fuera del marco de su padre? Porque cuando intento esto, mi UIButton parece no poder recibir ningún evento. ¿Cómo trabajo alrededor de esto?interacción más allá de los límites de uiview

+2

Esto también es bueno y relevante (o tal vez una víctima): http://stackoverflow.com/a/31420820/8047 –

+0

Esto funciona perfectamente para mí. https://developer.apple.com/library/ios/qa/qa2013/qa1812.html Best –

Respuesta

79

Sí. Puede anular el método hitTest:withEvent: para devolver una vista para un conjunto de puntos más grande que el que contiene la vista. Vea el UIView Class Reference.

Editar: Ejemplo:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(-radius, -radius, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return self; 
    } 
    return nil; 
} 

Edición 2: (Después de la clarificación :) Con el fin de asegurar que el botón se trata como si fuera dentro de los límites de los padres, se debe redefinir pointInside:withEvent: en el padre para incluir el marco del botón.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    if (CGRectContainsPoint(self.view.bounds, point) || 
     CGRectContainsPoint(button.view.frame, point)) 
    { 
     return YES; 
    } 
    return NO; 
} 

Nota el código sólo existe para anular pointInside no es del todo correcta. Como Invocar explica a continuación, hacer esto:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
    { 
    if (CGRectContainsPoint(self.oversizeButton.frame, point)) 
     return YES; 

    return [super pointInside:point withEvent:event]; 
    } 

Tenga en cuenta que habías muy probable que lo hace con self.oversizeButton como IBOutlet en esta subclase UIView; entonces puedes simplemente arrastrar el "botón de gran tamaño" en cuestión, hacia la vista especial en cuestión. (O, si por alguna razón ha estado haciendo esto mucho en un proyecto, tendría una subclase UIButton especial, y podría mirar a través de su lista de subvista para esas clases.) Espero que ayude.

+0

¿Puede ayudarme a implementar esto correctamente con algún código de muestra? También leo la referencia y debido a la siguiente línea, estoy confundido: "Los puntos que se encuentran fuera de los límites del receptor nunca se informan como aciertos, incluso si se encuentran dentro de una de las subvistas del receptor. Las subvistas pueden extenderse visualmente más allá de los límites. de su padre si la propiedad clipsToBounds de la vista principal está establecida en NO. Sin embargo, la prueba de impacto siempre ignora los puntos fuera de los límites de la vista principal." – ThomasM

+0

Especialmente esta parte hace que parezca que esta no es la solución que necesito:" Sin embargo, la prueba de impacto siempre ignora puntos fuera de los límites de la vista principal "... Pero podría estar equivocado, por supuesto, a veces encuentro las referencias de la manzana realmente confuso ... – ThomasM

+0

Ah, ya lo entiendo. Necesitas anular el 'pointInside: withEvent:' de los padres para incluir el marco del botón. Añadiré un código de muestra a mi respuesta anterior. – jnic

21

@jnic, estoy trabajando en iOS SDK 5.0 y con el fin de obtener su código de trabajo que tenía derecho a hacer esto:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 
if (CGRectContainsPoint(button.frame, point)) { 
    return YES; 
} 
return [super pointInside:point withEvent:event]; } 

La vista recipiente en mi caso es un UIButton y todos los elementos secundarios también son UIButtons que pueden moverse fuera de los límites del UIButton padre.

Mejor

6

Swift:

Dónde TargetView es la vista que desea recibir el evento (que puede ser total o parcialmente situado fuera del marco de la vista padre).

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { 
     let pointForTargetView: CGPoint = targetView.convertPoint(point, fromView: self) 
     if CGRectContainsPoint(targetView.bounds, pointForTargetView) { 
      return closeButton 
     } 
     return super.hitTest(point, withEvent: event) 
} 
13

En el padre vista se puede anular el método de prueba de posicionamiento:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGPoint translatedPoint = [_myButton convertPoint:point fromView:self]; 

    if (CGRectContainsPoint(_myButton.bounds, translatedPoint)) { 
     return [_myButton hitTest:translatedPoint withEvent:event]; 
    } 
    return [super hitTest:point withEvent:event]; 

} 

En este caso, si el punto cae dentro de los límites de su botón, reenvía la llamada allí; si no, vuelva a la implementación original.

2

En mi caso, tuve una subclase UICollectionViewCell que contenía UIButton. Inhabilité clipsToBounds en la celda y el botón estaba visible fuera de los límites de la celda. Sin embargo, el botón no estaba recibiendo eventos táctiles.Yo era capaz de detectar los eventos de toque en el botón mediante la respuesta de Jack: https://stackoverflow.com/a/30431157/3344977

Aquí hay una versión Swift:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { 

    let translatedPoint = button.convertPoint(point, fromView: self) 

    if (CGRectContainsPoint(button.bounds, translatedPoint)) { 
     print("Your button was pressed") 
     return button.hitTest(translatedPoint, withEvent: event) 
    } 
    return super.hitTest(point, withEvent: event) 
} 
0

Para mí (como para otros), la respuesta fue no para anular el hitTest método, sino más bien el método pointInside. Tenía que hacer esto solo en dos lugares.

El Superview Esta es la opinión de que si tuviera clipsToBounds establece en true, que haría todo este problema desaparece. Así que aquí es mi sencilla UIView en Swift 3:

class PromiscuousView : UIView { 
    override func point(inside point: CGPoint, 
       with event: UIEvent?) -> Bool { 
     return true 
    } 
} 

la subvista Su kilometraje puede variar, pero en mi caso también tuve que sobreescribir el método pointInside para la subvista que se había decidido el toque estaba fuera de límites. El mío es en Objective-C, por lo que:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 
    WEPopoverController *controller = (id) self.delegate; 
    if (self.takeHitsOutside) { 
     return YES; 
    } else { 
     return [super pointInside:point withEvent:event]; 
    } 
} 

This answer (y algunos otros en la misma pregunta) puede ayudar a aclarar su comprensión.

1

¿Por qué sucede esto?

Esto se debe a que cuando su subvista se encuentra fuera de los límites de su supervista, los eventos que realmente suceden en esa subvista no se entregarán a esa subvista. Sin embargo, se entregará WILL a su super visión.

Independientemente de si las subvistas se recortan visualmente o no, los eventos táctiles siempre respetan los límites del rectángulo de la supervista de la vista de destino. En otras palabras, los eventos táctiles que ocurren en una parte de una vista que se encuentra fuera del rectángulo de límites de su supervista no se entregan a esa vista. Link

Lo que hay que hacer?

Cuando su supervista recibe el evento táctil mencionado anteriormente, tendrá que decirle a UIKit explícitamente que mi subvista debe ser la que reciba este evento táctil.

¿Qué pasa con el código?

En su supervista, implementar func hitTest(_ point: CGPoint, with event: UIEvent?)

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 
     if isHidden || alpha == 0 || clipsToBounds { return nil } 
     // convert the point into subview's coordinate system 
     let subviewPoint = self.convert(point, to: subview) 
     // if the converted point lies in subview's bound, tell UIKit that subview should be the one that receives this event 
     if ! subview.isHidden && subview.bounds.contains(subviewPoint) { return subview } 
     return nil 
    } 
+1

¡Gracias por esto! He pasado horas y horas sobre un problema derivado de esto. Realmente aprecio que se tome el tiempo para publicar esto. :) – ClayJ

Cuestiones relacionadas