web-dev-qa-db-fra.com

Comment puis-je parcourir toutes les sous-vues d'un UIView, leurs sous-vues et leurs sous-vues

Comment puis-je parcourir toutes les sous-vues d'un UIView, leurs sous-vues et leurs sous-vues?

76
123hal321

Utiliser la récursivité:

// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy;
@end

// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy
{
    NSLog(@"%@", self);
    for (UIView *subview in self.subviews)
    {
        [subview logViewHierarchy];
    }
}
@end

// In your implementation
[myView logViewHierarchy];
120
Ole Begemann

Eh bien voici ma solution utilisant la récursivité et un wrapper (catégorie/extension) pour la classe UIView.

// UIView+viewRecursion.h
@interface UIView (viewRecursion)
- (NSMutableArray*) allSubViews;
@end

// UIView+viewRecursion.m
@implementation UIView (viewRecursion)
- (NSMutableArray*)allSubViews
{
   NSMutableArray *arr=[[[NSMutableArray alloc] init] autorelease];
   [arr addObject:self];
   for (UIView *subview in self.subviews)
   {
     [arr addObjectsFromArray:(NSArray*)[subview allSubViews]];
   }
   return arr;
}
@end

Utilisation: Vous devriez maintenant parcourir toutes les sous-vues et les manipuler selon vos besoins.

//disable all text fields
for(UIView *v in [self.view allSubViews])
{
     if([v isKindOfClass:[UITextField class]])
     {
         ((UITextField*)v).enabled=NO;
     }
}
33

Une solution dans Swift 3 qui donne tout subviews sans inclure la vue elle-même:

extension UIView {
var allSubViews : [UIView] {

        var array = [self.subviews].flatMap {$0}

        array.forEach { array.append(contentsOf: $0.allSubViews) }

        return array
    }
}
14
ielyamani

Je viens de trouver un moyen intéressant de le faire via le débogueur:

http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/

fait référence à ceci Apple Note technique:

https://developer.Apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT

Assurez-vous simplement que votre débogueur est en pause (définissez un point d'arrêt ou de le mettre en pause manuellement) et vous pouvez demander le recursiveDescription.

12
djibouti33

Je marque tout quand il est créé. Ensuite, il est facile de trouver n'importe quelle sous-vue.

view = [aView viewWithTag:tag];

Avec l'aide d'Ole Begemann. J'ai ajouté quelques lignes pour incorporer le concept de bloc.

UIView + HierarchyLogging.h

typedef void (^ViewActionBlock_t)(UIView *);
@interface UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction;
@end

UIView + HierarchyLogging.m

@implementation UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction {
    //view action block - freedom to the caller
    viewAction(self);

    for (UIView *subview in self.subviews) {
        [subview logViewHierarchy:viewAction];
    }
}
@end

Utilisation de la catégorie HierarchyLogging dans votre ViewController. Vous êtes maintenant libre de ce que vous devez faire.

void (^ViewActionBlock)(UIView *) = ^(UIView *view) {
    if ([view isKindOfClass:[UIButton class]]) {
        NSLog(@"%@", view);
    }
};
[self.view logViewHierarchy: ViewActionBlock];
7
docchang

Voici une autre implémentation Swift:

extension UIView {
    var allSubviews: [UIView] {
        return self.subviews.flatMap { [$0] + $0.allSubviews }
    }
}
7
Cal Stephens

Voici un exemple avec la fonctionnalité de bouclage et de coupure de vue réelle.

Swift:

extension UIView {

    func loopViewHierarchy(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        var stop = false
        block(self, &stop)
        if !stop {
            self.subviews.forEach { $0.loopViewHierarchy(block: block) }
        }
    }

}

Exemple d'appel:

mainView.loopViewHierarchy { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}

Boucle inversée:

extension UIView {

    func loopViewHierarchyReversed(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        for i in stride(from: self.highestViewLevel(view: self), through: 1, by: -1) {
            let stop = self.loopView(view: self, level: i, block: block)
            if stop {
                break
            }
        }
    }

    private func loopView(view: UIView, level: Int, block: (_ view: UIView, _ stop: inout Bool) -> ()) -> Bool {
        if level == 1 {
            var stop = false
            block(view, &stop)
            return stop
        } else if level > 1 {
            for subview in view.subviews.reversed() {
            let stop = self.loopView(view: subview, level: level - 1, block: block)
                if stop {
                    return stop
                }
            }
        }
        return false
    }

    private func highestViewLevel(view: UIView) -> Int {
        var highestLevelForView = 0
        for subview in view.subviews.reversed() {
            let highestLevelForSubview = self.highestViewLevel(view: subview)
            highestLevelForView = max(highestLevelForView, highestLevelForSubview)
        }
        return highestLevelForView + 1
    }
}

Exemple d'appel:

mainView.loopViewHierarchyReversed { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}

Objective-C:

typedef void(^ViewBlock)(UIView* view, BOOL* stop);

@interface UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block;
@end

@implementation UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block {
    BOOL stop = NO;
    if (block) {
        block(self, &stop);
    }
    if (!stop) {
        for (UIView* subview in self.subviews) {
            [subview loopViewHierarchy:block];
        }
    }
}
@end

Exemple d'appel:

[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) {
    if ([view isKindOfClass:[UIButton class]]) {
        /// use the view
        *stop = YES;
    }
}];
5
Vahram Dodoryan

Il n'est pas nécessaire de créer une nouvelle fonction. Faites-le simplement lors du débogage avec Xcode.

Définissez un point d'arrêt dans un contrôleur de vue et mettez l'application en pause à ce point d'arrêt.

Cliquez avec le bouton droit sur la zone vide et appuyez sur "Ajouter une expression ..." dans la fenêtre de surveillance de Xcode.

Entrez cette ligne:

(NSString*)[self->_view recursiveDescription]

Si la valeur est trop longue, cliquez dessus avec le bouton droit de la souris et choisissez "Imprimer la description de ...". Vous verrez toutes les sous-vues de self.view dans la fenêtre de la console. Changez auto -> _ view en autre chose si vous ne voulez pas voir les sous-vues de self.view.

Terminé! Pas de gdb!

4
Vince Yuan

Voici un code récursif: -

 for (UIView *subViews in yourView.subviews) {
    [self removSubviews:subViews];

}   

-(void)removSubviews:(UIView *)subView
{
   if (subView.subviews.count>0) {
     for (UIView *subViews in subView.subviews) {

        [self removSubviews:subViews];
     }
  }
  else
  {
     NSLog(@"%i",subView.subviews.count);
    [subView removeFromSuperview];
  }
}
4
Leena

A propos, j'ai créé un projet open source pour aider à ce genre de tâche. C'est très simple, et utilise des blocs Objective-C 2.0 pour exécuter du code sur toutes les vues d'une hiérarchie.

https://github.com/egold/UIViewRecursion

Exemple:

-(void)makeAllSubviewsGreen
{
    [self.view runBlockOnAllSubviews:^(UIView *view) {

        view.backgroundColor = [UIColor greenColor];
    }];
}
3
Eric Goldberg

Voici une variation sur réponse d'Ole Begemann ci-dessus, qui ajoute une indentation pour illustrer la hiérarchie:

// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces;
@end

// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces {
    if (whiteSpaces == nil) {
        whiteSpaces = [NSString string];
    }
    NSLog(@"%@%@", whiteSpaces, self);

    NSString *adjustedWhiteSpaces = [whiteSpaces stringByAppendingFormat:@"    "];

    for (UIView *subview in self.subviews) {
        [subview logViewHierarchy:adjustedWhiteSpaces];
    }
}
@end
2
jaredsinclair

Le code publié dans cette réponse traverse toutes les fenêtres et toutes les vues et toutes leurs sous-vues. Il a été utilisé pour sauvegarder une impression de la hiérarchie de vues dans NSLog, mais vous pouvez l'utiliser comme base pour toute traversée de la hiérarchie de vues. Il utilise une fonction C récursive pour parcourir l'arborescence de la vue.

1
progrmr

J'ai écrit ne catégorie quelque temps en arrière pour déboguer quelques vues.

IIRC, le code affiché est celui qui a fonctionné. Sinon, cela vous orientera dans la bonne direction. Utiliser à vos propres risques, etc.

0
TechZen

J'aurais aimé trouver cette page d'abord, mais si (pour une raison quelconque) vous voulez le faire de manière non récursive, pas dans une catégorie et avec plus de lignes de code

0
smlxl

Cela affiche également le niveau hiérarchique

@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(int)level
{
    NSLog(@"%d - %@", level, self);
    for (UIView *subview in self.subviews)
    {
        [subview logViewHierarchy:(level+1)];
    }
}
@end
0
snez

Je pense que toutes les réponses utilisant la récursivité (à l'exception de l'option de débogage) utilisaient des catégories. Si vous n'avez pas besoin ou ne voulez pas d'une catégorie, vous pouvez simplement utiliser une méthode d'instance. Par exemple, si vous devez obtenir un tableau de toutes les étiquettes dans votre hiérarchie de vues, vous pouvez le faire.

@interface MyViewController ()
@property (nonatomic, retain) NSMutableArray* labelsArray;
@end

@implementation MyViewController

- (void)recursiveFindLabelsInView:(UIView*)inView
{
    for (UIView *view in inView.subviews)
    {
        if([view isKindOfClass:[UILabel class]])
           [self.labelsArray addObject: view];
        else
           [self recursiveFindLabelsInView:view];
    }
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.labelsArray = [[NSMutableArray alloc] init];
    [self recursiveFindLabelsInView:self.view];

    for (UILabel *lbl in self.labelsArray)
    {
        //Do something with labels
    }
}
0
Donovan