web-dev-qa-db-fra.com

En passant par les touches de UIViews dessous

J'ai un UIView avec 4 boutons et un autre UIView au-dessus de la vue des boutons. La vue la plus haute contient un UIImageView avec un UITapGestureRecognizer.

Le comportement que j'essaie de créer est que lorsque l'utilisateur appuie sur la touche UIImageView, il bascule entre être petit dans le coin inférieur droit de l'écran et animer pour l'agrandir. Quand il est grand, je veux que les boutons de la vue du bas soient désactivés et quand il est petit et dans le coin inférieur droit, je veux que les touches soient transmises aux boutons et qu’ils fonctionnent normalement. J'y suis presque mais je ne parviens pas à faire passer les touches aux boutons à moins que je ne désactive le UserInteractions de la vue de dessus.

J'ai ceci dans mon initWithFrame: de la vue de dessus:

// 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];

et moi c'est mon imageTapped: méthode:

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

Avec le code ci-dessus, lorsque l'image est grande, les boutons sont inactifs, lorsque je touche l'image, elle se réduit et les boutons deviennent actifs. Cependant, la petite image ne reçoit pas les touches et ne sera donc pas étendue.

Si je mets self.userInteractionEnabled = YES dans les deux cas, l’image s’agrandit et se contracte dès que vous touchez, mais les boutons ne reçoivent jamais de touches et agissent comme s’ils étaient désactivés.

Y at-il moyen d’obtenir que l’image s’agrandisse et se contracte lorsqu’on la touche, mais que les boutons du dessous ne reçoivent les touches que si l’image est dans son état contracté? Est-ce que je fais quelque chose de stupide ici et que je manque quelque chose d'évident?

Je vais devenir complètement fou en essayant de faire en sorte que cela fonctionne, alors toute aide serait appréciée,

Dave

PDATE:

Pour d'autres tests, j'ai remplacé le touchesBegan: et touchesCancelled: méthodes et appelé leurs super implémentations sur ma vue contenant le UIImageView. Avec le code ci-dessus, le touchesCancelled: n'est jamais appelé et le touchesBegan: s'appelle toujours.

Donc, il semblerait que la vue reçoive les touches, elles ne sont tout simplement pas transmises à la vue en dessous.

MISE À JOUR

Est-ce à cause de la façon dont la chaîne de répondeurs fonctionne? Ma hiérarchie de vues ressemble à ceci:

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

Je pense que le système d’exploitation effectue d’abord un test de test, comme indiqué par View2, il devrait donc recevoir tous les contacts et ceux-ci ne sont jamais transmis à View3, sauf si userInteractions est défini sur NO pour View2, auquel cas l’imageView1 est également empêchée de recevoir des contacts. Est-ce ainsi que cela fonctionne et existe-t-il un moyen pour View2 de traverser ses contacts avec View3?

64
Magic Bullet Dave

Le UIGestureRecognizer est un hareng rouge, je pense. En fin de compte, pour résoudre ce problème, j’ai annulé le pointInside:withEvent: méthode de mon UIView:

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

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

    return pointInside;
}

Cela provoque la capture de toutes les touches de la vue si vous touchez l'imageView ou si son indicateur développé est défini. S'il n'est pas développé, ne capturez que les touches si elles sont sur l'imageView.

En renvoyant NON, la vue de niveau supérieur du VC interroge le reste de sa hiérarchie de vues à la recherche d'un hit.

91
Magic Bullet Dave

Sélectionnez votre View dans Storyboard ou XIB et ...


enter image description here


ou en Swift

view.isUserInteractionEnabled = false
59
Michael

Regardez dans le protocole UIGestureRecognizerDelegate . Plus précisément, gestureRecognizer:shouldReceiveTouch:

Vous voudrez faire de chaque UIGestureRecognizer une propriété de votre UIViewController,

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

// .m
@synthesize lowerTap;

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

Assurez-vous de faire de votre UIViewController un délégué,

[self.lowerTap setDelegate: self];

Ensuite, vous auriez quelque chose comme ça,

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

Bien sûr, ce n'est pas le code exact. Mais c’est le schéma général que vous voudriez suivre.

6
john

@Magic Bullet La solution de Dave mais dans 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
}

Je l'utilise dans CameraOverlayView pour ImagePickerViewController.cameraOverlay pour permettre à l'utilisateur de commenter tout en prenant une nouvelle photo.

3
imike

J'ai une autre solution. J'ai deux points de vue, appelons-les CustomSubView qui se chevauchent et ils devraient recevoir les touches. J'ai donc un contrôleur de vue et une classe UIView personnalisée, appelons-le ViewControllerView que j'ai défini dans le générateur d'interface, puis j'ai ajouté les deux vues qui devraient recevoir le contact avec cette vue.

J'ai donc intercepté les touches dans ViewControllerView en écrasant hitTest:

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

Puis j'ai écrasé dans 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];
        }
    }
}

Faites exactement la même chose avec touchesMovedtouchesEnded et touchesCancelled.

3
Nico S.
// 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
Anton Tropashko