web-dev-qa-db-de.com

Wie kann ich alle Unteransichten einer UIView sowie deren Unteransichten und Unteransichten durchlaufen?

Wie kann ich alle Unteransichten einer UIView und deren Unteransichten und Unteransichten durchlaufen?

76
123hal321

Rekursion verwenden:

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

Nun, hier ist meine Lösung mit Rekursion und einem Wrapper (Kategorie/Erweiterung) für die UIView-Klasse.

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

Verwendung: Jetzt sollten Sie alle Unteransichten durchlaufen und nach Bedarf bearbeiten.

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

Eine Lösung in Swift 3, die alle subviews ohne die Ansicht selbst gibt:

extension UIView {
var allSubViews : [UIView] {

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

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

        return array
    }
}
14
ielyamani

Wir haben gerade einen interessanten Weg gefunden, dies über den Debugger zu tun:

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

verweist auf diese Apple Technote:

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

Stellen Sie einfach sicher, dass Ihr Debugger angehalten ist (setzen Sie entweder manuell einen Haltepunkt) und Sie können nach dem recursiveDescription fragen.

12
djibouti33

Ich markiere alles, wenn es erstellt wird. Dann ist es einfach, eine Unteransicht zu finden.

view = [aView viewWithTag:tag];

Mit der Hilfe von Ole Begemann. Ich habe ein paar Zeilen hinzugefügt, um das Blockkonzept darin zu integrieren.

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

Verwenden der HierarchyLogging-Kategorie in Ihrem ViewController. Sie haben jetzt die Freiheit, was Sie tun müssen.

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

Hier ist eine weitere Implementierung von Swift:

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

Hier ist ein Beispiel mit Funktionen zum Durchlaufen und Unterbrechen von Ansichten.

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) }
        }
    }

}

Aufrufbeispiel:

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

Umgekehrte Schleife:

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

Aufrufbeispiel:

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

Ziel-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

Aufrufbeispiel:

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

Es muss keine neue Funktion erstellt werden. Tun Sie es einfach, wenn Sie mit Xcode debuggen.

Legen Sie einen Haltepunkt in einem Ansichts-Controller fest und halten Sie die App an diesem Haltepunkt an.

Klicken Sie mit der rechten Maustaste auf den leeren Bereich und drücken Sie im Überwachungsfenster von Xcode auf "Ausdruck hinzufügen ...".

Geben Sie diese Zeile ein:

(NSString*)[self->_view recursiveDescription]

Wenn der Wert zu lang ist, klicken Sie mit der rechten Maustaste darauf und wählen Sie "Beschreibung drucken von ...". Sie sehen alle Unteransichten von self.view im Konsolenfenster. Ändern Sie die Ansicht self -> _ in etwas anderes, wenn Sie keine Unteransichten von self.view sehen möchten.

Getan! Kein gdb!

4
Vince Yuan

Hier ist ein rekursiver Code: -

 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

Übrigens habe ich ein Open-Source-Projekt erstellt, um bei dieser Art von Aufgabe zu helfen. Es ist wirklich einfach und verwendet Objective-C 2.0-Blöcke, um Code für alle Ansichten in einer Hierarchie auszuführen.

https://github.com/egold/UIViewRecursion

Beispiel:

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

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

Hier ist eine Variation von Ole Begemanns Antwort oben, die Einrückungen hinzufügt, um die Hierarchie zu veranschaulichen:

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

Der Code in diese Antwort durchläuft alle Fenster und alle Ansichten und alle ihre Unteransichten. Es wurde verwendet, um einen Ausdruck der Ansichtshierarchie in NSLog zu sichern, aber Sie können ihn als Grundlage für ein Durchlaufen der Ansichtshierarchie verwenden. Es verwendet eine rekursive C-Funktion, um den Ansichtsbaum zu durchlaufen.

1
progrmr

Ich schrieb eine Kategorie einige Zeit zurück, um einige Ansichten zu debuggen.

IIRC, die Postleitzahl ist diejenige, die funktioniert hat. Wenn nicht, zeigt es Ihnen in die richtige Richtung. Verwendung auf eigenes Risiko usw.

0
TechZen

Ich wünschte, ich hätte diese Seite zuerst gefunden, aber wenn Sie dies (aus irgendeinem Grund) nicht rekursiv tun möchten, nicht in einer Kategorie und mit mehr Codezeilen

0
smlxl

Dies zeigt auch die Hierarchieebene an

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

Ich denke, alle Antworten mit Rekursion (mit Ausnahme der Debugger-Option) verwendeten Kategorien. Wenn Sie keine Kategorie benötigen/möchten, können Sie einfach eine Instanzmethode verwenden. Wenn Sie beispielsweise ein Array aller Beschriftungen in Ihrer Ansichtshierarchie benötigen, können Sie dies tun.

@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