web-dev-qa-db-fra.com

Comment désactiver la saisie tactile pour toutes les vues à l'exception de la vue la plus haute?

J'ai une vue avec plusieurs sous-vues. Lorsqu'un utilisateur touche une sous-vue, la taille de la sous-vue s'étend pour couvrir la majeure partie de l'écran, mais certaines des autres sous-vues sont toujours visibles en dessous.

Je veux que mon application ignore les touches sur les autres sous-vues lorsque l'une des sous-vues est "développée" comme ceci. Existe-t-il un moyen simple d'y parvenir? Je peux écrire du code pour gérer cela, mais j'espérais qu'il existe une méthode intégrée plus simple.

42
MusiGenesis

J'espère que cette aide ...

[[yourSuperView subviews]
   makeObjectsPerformSelector:@selector(setUserInteractionEnabled:)
   withObject:[NSNumber numberWithBool:FALSE]];

qui désactivera UserInteraction des sous-vues immédiates d'une vue .. Ensuite, donnez UserInteraction à la seule vue que vous vouliez

yourTouchableView.setUserInteraction = TRUE;

MODIFIER:

Il semble que dans iOS, la désactivation de userInteraction sur une vue parent ne désactive pas userInteraction sur ses enfants. Donc le code ci-dessus (je veux dire celui avecmakeObjectsPerformSelector:) ne fonctionnera que pour désactiver l'interaction utilisateur des sous-vues immédiates d'un parent ..

Voir la réponse de l'utilisateur madewulf qui obtient récursivement toutes les sous-vues et désactive l'interaction utilisateur de chacune d'entre elles. Ou si vous devez désactiver userInteraction de cette vue à de nombreux endroits du projet, vous pouvez classer UIView pour ajouter cette fonctionnalité .. Quelque chose comme ça fera ..

@interface UIView (UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value;
@end

@implementation UIView(UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value{
    self.userInteractionEnabled =   value;
    for (UIView *view in [self subviews]) {
        [view setRecursiveUserInteraction:value];
    }
}
@end

Vous pouvez maintenant appeler

[yourSuperView setRecursiveUserInteraction:NO];

La suggestion de l'utilisateur @ lxt d'ajouter une vue invisible au-dessus de toutes les vues est également une autre façon de le faire.

53
Krishnabhadra

Il y a plusieurs façons de procéder. Vous pouvez parcourir toutes vos autres sous-vues et définir userInteractionEnabled = NO, mais ce n'est pas l'idéal si vous avez beaucoup d'autres vues (vous devrez, après tout, les renommer toutes par la suite).

La façon dont je fais cela est de créer une UIView invisible qui est la taille de tout l'écran qui "bloque" toutes les touches d'aller aux autres vues. Parfois, cela est littéralement invisible, d'autres fois je peux le mettre en noir avec une valeur alpha de 0,3 ou plus.

Lorsque vous développez votre sous-vue principale pour remplir l'écran, vous pouvez ajouter cette vue UIV "bloquante" derrière elle (en utilisant insertSubview: belowSubview:). Lorsque vous réduisez votre sous-vue étendue, vous pouvez supprimer l'UIView invisible de votre hiérarchie.

Donc pas tout à fait intégré, mais je pense que l'approche la plus simple. Je ne sais pas si c'est à cela que vous pensiez déjà, j'espère que cela vous sera utile.

46
lxt

Méfiez-vous du code donné ici comme solution par Krishnabhadra:

[[yourSuperView subviews]makeObjectsPerformSelector:@selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:FALSE]];

Cela ne fonctionnera pas dans tous les cas, car [vos sous-vues SuperView] ne donne que les sous-vues directes de la vue d'ensemble. Pour le faire fonctionner, vous devrez itérer récursivement sur toutes les sous-vues:

-(void) disableRecursivelyAllSubviews:(UIView *) theView
{
    theView.userInteractionEnabled = NO;
    for(UIView* subview in [theView subviews])
    {
        [self disableRecursivelyAllSubviews:subview];
    }
}

-(void) disableAllSubviewsOf:(UIView *) theView
{
    for(UIView* subview in [theView subviews])
    {
        [self disableRecursivelyAllSubviews:subview];
    }
} 

Maintenant, un appel à disableAllSubviewsOf fera ce que vous vouliez faire.

Si vous avez une pile de vues approfondie, la solution de lxt est probablement meilleure.

8
madewulf

Je le ferais en mettant un bouton transparent personnalisé avec le même cadre que le superView. Et puis au-dessus de ce bouton, je mettrais une vue qui devrait accepter les touches de l'utilisateur. Le bouton avalera toutes les touches et les vues derrière ne recevront aucun événement tactile, mais la vue au-dessus du bouton recevra les touches normalement.

Quelque chose comme ça:

- (void)disableTouchesOnView:(UIView *)view {
    UIButton *ghostButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)];
    [ghostButton setBackgroundColor:[UIColor clearColor]];
    ghostButton.tag = 42; // Any random number. Use #define to avoid putting numbers in code.

    [view addSubview:ghostButton];
}

Et une méthode pour activer le parentView.

- (void)enableTouchesOnView:(UIView *)view {
    [[view viewWithTag:42] removeFromSuperview];
}

Donc, pour désactiver toutes les vues dans le parentViev derrière yourView, je ferais ceci:

YourView *yourView = [[YourView alloc] initWithCustomInitializer];
// It is important to disable touches on the parent view before adding the top most view.
[self disableTouchesOnView:parentView];
[parentView addSubview:yourView];
5
JPetric

Juste parentView.UserInteractionEnabled = NO fera le travail. La vue parent désactivera l'interaction de l'utilisateur sur toutes les sous-vues de la vue. Mais activer il ne activer toutes les sous-vues (par défaut, UIImageView n'est pas interactif). Donc, un moyen simple est de trouver la vue parent et d'utiliser le code ci-dessus, et il n'est pas nécessaire d'itérer toutes les sous-vues pour effectuer un sélecteur.

4
musixlemon

Je donnerai mes 2 cents à ce problème. Exécutez itérativement userInteractionEnabled = false, c'est à sens unique. Une autre façon sera d'ajouter une UIView comme suit.

EZEventEater.h

#import <UIKit/UIKit.h>
@interface EZEventEater : UIView
@end

EZEventEater.m

#import "EZEventEater.h"
@implementation EZEventEater

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.backgroundColor = [UIColor clearColor];
        self.userInteractionEnabled = false;
    }
    return self;
}


- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   //EZDEBUG(@"eater touched");
}

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{

}

Dans votre code, vous ajoutez la vue EZEventEater pour couvrir toutes les vues susceptibles de bloquer l'événement tactile. Chaque fois que vous souhaitez bloquer l'événement tactile sur ces vues, appelez simplement

eater.userInteractionEnabled = YES;

J'espère que cela vous sera utile.

2
tianwalker

Ajoutez un TapGestureRecognizer à votre "vue d'arrière-plan" (la translucide qui "grise" votre interface normale) et réglez-la sur "Annule les touches en vue", sans ajouter d'action.

let captureTaps = UITapGestureRecognizer()
captureTaps.cancelsTouchesInView = true
dimmedOverlay?.addGestureRecognizer(captureTaps)
1
Robert Atkins

Pour mon application, je pense qu'il suffira de désactiver la navigation vers les autres onglets de l'application (pour une durée limitée, pendant que je fais un peu de traitement):

self.tabBarController.view.userInteractionEnabled = NO;

De plus, j'ai désactivé le contrôleur de vue actuel ...

self.view.userInteractionEnabled = NO;

(Et, en passant, les solutions récursives proposées ici ont eu des effets étranges dans mon application. La désactivation semble fonctionner correctement, mais la réactivation a des effets étranges - une partie de l'interface utilisateur n'a pas été réactivée).

0
Chris Prince

setUserInteractionEnabled = NO sur la vue que vous souhaitez désactiver

0
Kirby Todd