Pasando a través de toques a UIViews debajo


Tengo un UIView con 4 botones en él y otro UIView en la parte superior de la vista de botones. La vista superior contiene un UIImageView con un UITapGestureRecognizer en ella.

El comportamiento que estoy tratando de crear es que cuando el usuario toca el UIImageView alterna entre ser pequeño en la esquina inferior derecha de la pantalla y animar para hacerse más grande. Cuando es grande, quiero que los botones de la vista inferior se desactiven y cuando es pequeño y en la esquina inferior derecha quiero que los toques sean pasado a través de los botones y para que funcionen como de costumbre. Estoy casi allí, pero no puedo conseguir que los toques pasen a través de los botones a menos que desactive el UserInteractions de la vista superior.

Tengo esto en mi initWithFrame: de la vista superior:

// Add a gesture recognizer to the image view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageTapped:)];
tapGestureRecognizer.cancelsTouchesInView = NO;
[imageView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];

Y este es mi imageTapped: método:

- (void) imageTapped:(UITapGestureRecognizer *) gestureRecognizer {
    // Toggle between expanding and contracting the image
    if (expanded) {
        [self contractAnimated:YES];
        expanded = NO;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = NO;
        self.exclusiveTouch = NO;
    }
    else {
        [self expandAnimated:YES];
        expanded = YES;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = YES;
        self.exclusiveTouch = YES;
    }
}

Con el código anterior, cuando la imagen es grande los botones están inactivos, cuando toco la imagen se encoge y los botones se activan. Sin embargo, la imagen pequeña no recibe el toca y por lo tanto no se expandirá.

Si establezco self.userInteractionEnabled = YES en ambos casos, entonces la imagen se expande y se contrae cuando se toca, pero los botones nunca reciben toques y actúan como si estuvieran deshabilitados.

¿Hay una distancia para que la imagen se expanda y contraiga cuando se toca, pero para que los botones debajo solo reciban toques si la imagen está en su estado contraído? ¿Estoy haciendo algo estúpido aquí y me estoy perdiendo algo obvio?

Me estoy volviendo absolutamente loco tratando de conseguir esto trabajar para que cualquier ayuda sea apreciada,

Dave

ACTUALIZACIÓN:

Para pruebas adicionales, anulé los métodos touchesBegan: y touchesCancelled: y llamé a sus súper implementaciones en mi vista que contiene el UIImageView. Con el código anterior, el touchesCancelled: nunca se llama y el touchesBegan: siempre se llama.

Por lo que parece que la vista está recibiendo los toques, simplemente no se pasan a la vista inferior.

UPDATE

Es esto ¿por la forma en que funciona la cadena de respuesta? Mi jerarquía de vista se ve así:

VC - View1
     -View2
      -imageView1 (has tapGestureRecogniser)
      -imageView2
     -View3
      -button1
      -button2

Creo que el sistema operativo primero hace un hitTest como dice View2 está en frente por lo que debe obtener todos los toques y estos nunca se pasan a View3 a menos que userInteractions se establece en NO para View2, en cuyo caso el imageView1 también se impide recibir toques. ¿Es así como funciona y hay una manera de que View2 pase a través de sus toques a View3?

Author: KlimczakM, 2012-01-27

6 answers

El UIGestureRecognizer es una pista falsa, creo. Al final para resolver esto, anulé el método pointInside:withEvent: de mi UIView:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    BOOL pointInside = NO;

    if (CGRectContainsPoint(imageView.frame, point) || expanded) pointInside = YES;

    return pointInside;
}

Esto hace que la vista atrape todos los toques si toca la ImageView o si su bandera expandida está activada. Si no está expandido, solo atrape los toques si están en ImageView.

Al devolver NO, la vista de la VC de nivel superior consulta al resto de su jerarquía de vistas en busca de un resultado.

 83
Author: Magic Bullet Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-27 09:59:47

Seleccione su View en Storyboard o XIB y...


introduzca la descripción de la imagen aquí


O en Swift

view.isUserInteractionEnabled = false
 53
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 15:25:13

Busque en el Protocolo UIGestureRecognizerDelegate. Específicamente, gestureRecognizer:shouldReceiveTouch:

Usted querrá hacer cada UIGestureRecognizer una propiedad de su UIViewController,

// .h
@property (nonatomic, strong) UITapGestureRecognizer *lowerTap;

// .m
@synthesize lowerTap;

// When you are adding the gesture recognizer to the image view
self.lowerTap = tapGestureRecognizer

Asegúrese de que su UIViewController un delegado,

[self.lowerTap setDelegate: self];

Entonces, tendrías algo como esto, {[10]]}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if (expanded && gestureRecognizer == self.lowerTap) {    
        return NO;
    }
    else {
        return YES;
    }
}

Por supuesto, este no es el código exacto. Pero este es el patrón general que querrías seguir.

 5
Author: john,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-26 22:26:29

Tengo otra solución. Tengo dos puntos de vista, llamémoslos CustomSubView que se superponían y ambos deberían recibir los toques. Así que tengo un controlador de vista y una clase UIView personalizada, llamémoslo ViewControllerView que configuré en interface builder, luego agregué las dos vistas que deberían recibir los toques a esa vista.

Así que intercepté los toques en ViewControllerView sobrescribiendo hitTest:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return self;
}

Entonces sobrescribí en ViewControllerView:

- (void)touchesBegan:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    for (UIView *subview in [self.subviews reverseObjectEnumerator])
    {
        if ([subview isKindOfClass:[CustomSubView class]])
        {
            [subview touchesBegan:touches withEvent:event];
        }
    }
}

Haga exactamente lo mismo con touchesMoved touchesEnded y touchesCancelled.

 2
Author: Nico S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-16 09:22:12

@Magic Bullet La solución de Dave pero en Swift

Swift 3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    var pointInside = false
    if commentTextField.frame.contains(point) {
        pointInside = true
    } else {
        commentTextField.resignFirstResponder()
    }
    return pointInside
}

Lo uso en mi cameraOverlayView para ImagePickerViewController.cameraOverlay para dar al usuario la capacidad de comentar mientras toma una nueva foto

 0
Author: Mihael Isaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 23:00:49
// this is due to userInteractionEnabled deficiency: the flag stops traversal of subviews
@objc(YourObjcPrefixHereForXibAndStoryboardUseViewTransparentToTouch)
open class ViewTransparentToTouch: UIView
{
public var daisyView: UIView?
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let hitView = super.hitTest(point, with: event)
    if hitView === self {
        return daisyView
    }
    return hitView // childView
}
}
 0
Author: Anton Tropashko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-13 14:55:17