Pregunta interacción más allá de los límites de uiview


¿Es posible que un UIButton (o cualquier otro control para ese asunto) 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?


75
2018-03-25 13:21


origen


Respuestas:


Sí. Puede anular el hitTest:withEvent: método para devolver una vista para un conjunto mayor de puntos que contiene esa vista. Ver el Referencia de clase UIView.

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;
}

Editar 2: (Después de la aclaración :) Para asegurarse de que el botón se trata como dentro de los límites de los padres, debe anular 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 justo allí para anular el pointInside no es del todo correcto. Como Summon explica a continuación, haz 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 es muy probable que lo haga con self.oversizeButton como IBOutlet en esta subclase de 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.


83
2018-03-25 13:36



@jnic, estoy trabajando en iOS SDK 5.0 y para que tu código funcione correctamente tuve que hacer esto:

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

La vista de contenedor 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


23
2018-02-02 14:22



En la vista principal, puede anular el método de prueba de impacto:

- (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.


16
2018-05-25 04:17



Rápido:

Donde targetView es la vista que desea recibir el evento (que puede estar total o parcialmente ubicado fuera del marco de su vista principal).

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)
}

7
2018-04-25 19:26



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

Aquí hay una versión de 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)
}

5
2017-11-30 23:29



¿Por qué está pasando esto?

Esto se debe a que cuando su subvista se encuentra fuera de los límites de su supervista, los eventos táctiles que realmente suceden en esa subvista no se enviarán a esa subvista. De todos modos, eso SERÁ ser entregado a su supervista.

Independientemente de si las subvistas se recortan visualmente o no, los eventos táctiles siempre respetan el rectángulo de límites de la supervista de la vista objetivo. 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. Enlazar

¿Qué necesitas hacer?

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

¿Qué hay del código?

En su super visión, implemente 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
    }

2
2018-01-10 22:46



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

La Supervista Esta es la opinión de que si tuviera clipsToBounds establecido en verdadero, haría desaparecer todo este problema. Así que aquí está mi simple 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 sobrescribir el pointInside método para la subvista que se había decidido que el toque estaba fuera de límites. El mío está en Objective-C, entonces:

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

Esta respuesta (y algunos otros sobre la misma pregunta) pueden ayudar a aclarar su comprensión.


0
2018-01-09 15:13