116 votes

Autoriser l'interaction avec un UIView sous un autre UIView

Est-il un moyen simple de permettre l'interaction avec un bouton dans une UIView qui se trouve sous un autre UIView - où il n'y a pas des objets réels à partir du haut UIView sur le dessus du bouton?

Par exemple, en ce moment j'ai une UIView (A) avec un objet en haut et un objet au fond de l'écran, et rien au milieu. Il repose sur le dessus de l'autre UIView qui a des boutons dans le milieu (B). Cependant, je n'arrive pas à interagir avec les boutons dans le milieu de B.

Je peux voir les boutons B - j'ai mis le fond d'Un de clearColor - mais les boutons en B ne semblent pas recevoir de touche malgré le fait qu'il n'y a pas d'objets à partir d'Une réalité au-dessus de ces boutons.

EDIT - je veux toujours être en mesure d'interagir avec les objets dans le top UIView

Il y a sûrement un moyen simple de faire cela?

96voto

gyim Points 3103

Vous devez créer une sous-classe UIView pour votre vue de dessus et remplacer la méthode suivante:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    // UIView will be "transparent" for touch events if we return NO
    return (point.y < MIDDLE_Y1 || point.y > MIDDLE_Y2);
}
 

Vous pouvez également consulter la méthode hitTest: event:.

44voto

Stuart Points 8578

Alors que beaucoup de réponses ici, je suis un peu surpris de voir que le plus pratique, générique et infaillible réponse n'a pas été donné ici. @Ash est venu le plus proche, sauf qu'il y a quelque chose d'étrange avec le retour de la superview... ne pas le faire.

Cette réponse est prise à partir d'une réponse que j'ai donné à une question similaire, ici.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self) return nil;
    return hitView;
}

[super hitTest:point withEvent:event] sera de retour le plus profond de l'avis en vue de la hiérarchie qui a été touché. Si hitView == self (c'est à dire si il n'y a pas de sous-vue sous le point de contact), de retour nil, en précisant que ce point de vue devrait pas recevoir le toucher. La façon dont l'intervenant de la chaîne d'œuvres signifie que le point de vue de la hiérarchie au-dessus de ce point continuera à être parcouru jusqu'à ce qu'une vue est trouvée pour répondre à la toucher. Ne pas retourner le superview, comme il n'est pas jusqu'à ce point de vue, si son superview doit accepter touche ou pas!

Cette solution est:

  • pratique, car il ne nécessite pas de références à toutes les autres vues/sous-vues/objets;
  • générique, car il s'applique à tout point de vue que des actes purement comme un conteneur pour toucher des sous-vues, et de la configuration de la sous-vues n'affecte pas la façon dont il fonctionne (comme il le fait si vous substituez pointInside:withEvent: - retour d'un particulier palpable de la zone).
  • à toute épreuve, il n'y a pas beaucoup de code... et le concept n'est pas difficile à obtenir autour de votre tête.

Je l'utiliser assez souvent que j'ai captée dans une sous-classe pour enregistrer inutile afficher les sous-classes pour un remplacement. Comme un bonus, ajouter une propriété à le rendre configurable:

@interface ISView : UIView
@property(nonatomic, assign) BOOL onlyRespondToTouchesInSubviews;
@end

@implementation ISView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self && onlyRespondToTouchesInSubviews) return nil;
    return hitView;
}
@end

Alors lâchez-vous et utilisez cette vue où vous pouvez utiliser un simple UIView. La configuration est aussi simple que de créer onlyRespondToTouchesInSubviews de YES.

31voto

Tyler Points 16516

Il existe plusieurs façons vous pouvez gérer cela. Mon préféré est de remplacer hitTest:withEvent: dans une vue est une commune superview (peut-être indirectement) pour les points de vue contradictoires (on dirait que vous appelez ces A et B). Par exemple, quelque chose comme ceci (ici A et B sont UIView pointeurs, où B est le "caché", qui est normalement pris en compte):

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

    if ([B pointInside:pointInB withEvent:event])
        return B;

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

Vous pouvez également modifier l' pointInside:withEvent: méthode gyim suggéré. Cela vous permet d'obtenir essentiellement le même résultat en "piquer un trou" dans Un, au moins pour la touche finale.

Une autre approche est le réacheminement d'événement, ce qui signifie primordial touchesBegan:withEvent: et des méthodes similaires (comme touchesMoved:withEvent: etc) pour envoyer quelques touches à un objet différent de celui où ils ont d'abord aller. Par exemple, dans Un, vous pouvez écrire quelque chose comme ceci:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self shouldForwardTouches:touches]) {
        [B touchesBegan:touches withEvent:event];
    }
    else {
        // Do whatever A does with touches.
    }
}

Toutefois, cela ne fonctionne pas toujours comme vous le souhaitez! La chose principale est que des contrôles intégrés comme UIButton toujours ignorer transmis touche. De ce fait, la première approche est plus fiable.

Il y a un bon billet de blog expliquant tout cela plus en détail, avec un petit travail xcode projet de démonstration les idées, disponible ici:

http://bynomial.com/blog/?p=74

29voto

Benjamin Cox Points 4121

Vous devez définir upperView.userInteractionEnabled = NO; , sinon la vue supérieure interceptera les touches.

La version de Interface Builder utilisée est une case à cocher située au bas du panneau Attributs de la vue, intitulée "Interaction utilisateur activée". Décochez-la et vous devriez être prêt à partir.

11voto

samvermette Points 20225

L'implémentation personnalisée de pointInside: withEvent: semblait en effet être la voie à suivre, mais traiter avec des coordonnées codées en dur me semblait étrange. J'ai donc fini par vérifier si le CGPoint était à l'intérieur du bouton CGRect à l'aide de la fonction CGRectContainsPoint ():

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return (CGRectContainsPoint(disclosureButton.frame, point));
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X