Wenn Sie wirklich eine Schleife durchlaufen MÜSSEN, ist die akzeptierte Antwort korrekt. Wenn Sie nur nach einer einzelnen Ansicht suchen, markieren Sie sie und verwenden Sie stattdessen viewWithTag - sparen Sie sich etwas Schmerz! - developer.apple.com/library/ios/documentation/UIKit/Reference/…
Norman H
Antworten:
122
Rekursion verwenden:
// UIView+HierarchyLogging.h@interfaceUIView (ViewHierarchyLogging)
- (void)logViewHierarchy;
@end// UIView+HierarchyLogging.m@implementationUIView (ViewHierarchyLogging)
- (void)logViewHierarchy
{
NSLog(@"%@", self);
for (UIView *subview inself.subviews)
{
[subview logViewHierarchy];
}
}
@end// In your implementation
[myView logViewHierarchy];
Gibt es eine Möglichkeit, eine Funktion in der logViewHierarchy zu übergeben? Auf diese Weise kann es zu unterschiedlichen Zeiten Ihren Anforderungen entsprechen.
Docchang
2
@docchang: Obj-C-Blöcke eignen sich hervorragend für diesen Anwendungsfall. Sie übergeben einen Block an die Methode, führen den Block aus und übergeben ihn bei jedem Methodenaufruf in der Hierarchie.
Ole Begemann
Nett. Ich habe unten einen Ausschnitt gepostet, der dieser Technik Leerzeicheneinrückungen hinzufügt.
Jaredsinclair
34
Nun, hier ist meine Lösung mit Rekursion und einem Wrapper (Kategorie / Erweiterung) für die UIView-Klasse.
Beachten Sie, dass die allSubViewsFunktion einen Speicherverlust aufweist : Sie müssen entweder ein Array als [[[NSMutableArray alloc] init] autorelease]oder als erstellen [NSMutableArray array](was dasselbe ist).
Ivanzoid
1
Mit "ein Wrapper für UIView" meinen Sie tatsächlich "eine Kategorie für UIView".
Stellen Sie einfach sicher, dass Ihr Debugger angehalten ist (entweder setzen Sie einen Haltepunkt, um ihn manuell anzuhalten), und Sie können nach dem fragen recursiveDescription.
Sieht so aus, als hätte ich eine sehr ähnliche Lösung gefunden wie die, die Sie hier vorgeschlagen haben. Ich habe es auf Github gepostet, falls andere es nützlich finden könnten. Hier ist meine Antwort mit dem Link :) stackoverflow.com/a/10440510/553394
Eric Goldberg
Wie kann ich eine Möglichkeit hinzufügen, die Rekursion innerhalb des Blocks anzuhalten? Angenommen, ich verwende dies, um eine bestimmte Ansicht zu finden. Sobald ich sie gefunden habe, möchte ich dort anhalten und den Rest der Ansichtshierarchie nicht weiter durchsuchen.
Mic Pringle
4
Es muss keine neue Funktion erstellt werden. Tun Sie es einfach beim Debuggen mit Xcode.
Legen Sie einen Haltepunkt in einem Ansichts-Controller fest und lassen Sie die App an diesem Haltepunkt anhalten.
Klicken Sie mit der rechten Maustaste auf den leeren Bereich und klicken Sie im Xcode-Überwachungsfenster 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 von ... drucken". Sie sehen alle Unteransichten von self.view im Konsolenfenster. Ändern Sie die Ansicht self -> _ in eine andere Ansicht, wenn Sie keine Unteransichten von self.view anzeigen möchten.
Können Sie erklären, wo sich der "leere Bereich" befindet? Ich habe alles rund um das Xcode-Fenster versucht, sobald der Haltepunkt ausgelöst wurde, kann aber nicht finden, wo die Zeichenfolge
eingegeben werden soll
4
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];
}
}
hilfreiche Lösung und Zeitersparnis .. Danke 1 plus für u
Brainstorm Technolabs
3
Ü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.
Der in dieser Antwort veröffentlichte Code durchläuft alle Fenster und alle Ansichten sowie alle ihre Unteransichten. Es wurde verwendet, um einen Ausdruck der Ansichtshierarchie an NSLog zu senden, aber Sie können ihn als Grundlage für jede Durchquerung der Ansichtshierarchie verwenden. Es verwendet eine rekursive C-Funktion, um den Ansichtsbaum zu durchlaufen.
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
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 abrufen müssen, können Sie dies tun.
Die folgende Methode erstellt ein oder mehrere veränderbare Arrays und durchläuft dann die Unteransichten der Eingabeansicht. Dabei wird die anfängliche Unteransicht hinzugefügt und dann abgefragt, ob Unteransichten dieser Unteransicht vorhanden sind. Wenn true, ruft es sich erneut auf. Dies geschieht so lange, bis alle Ansichten der Hierarchie hinzugefügt wurden.
-(NSArray *)allSubs:(UIView *)view {
NSMutableArray * ma = [NSMutableArray new];
for (UIView * sub in view.subviews){
[ma addObject:sub];
if (sub.subviews){
[ma addObjectsFromArray:[self allSubs:sub]];
}
}
return ma;
}
Bitte benutzen Sie den Bearbeitungslink, um zu erklären, wie dieser Code funktioniert, und geben Sie nicht nur den Code an, da eine Erklärung eher zukünftigen Lesern helfen wird. Siehe auch Antworten . Quelle
Antworten:
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];
quelle
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 sie nach Bedarf bearbeiten.
//disable all text fields for(UIView *v in [self.view allSubViews]) { if([v isKindOfClass:[UITextField class]]) { ((UITextField*)v).enabled=NO; } }
quelle
allSubViews
Funktion einen Speicherverlust aufweist : Sie müssen entweder ein Array als[[[NSMutableArray alloc] init] autorelease]
oder als erstellen[NSMutableArray array]
(was dasselbe ist).Hier ist eine weitere Swift-Implementierung:
extension UIView { var allSubviews: [UIView] { return self.subviews.flatMap { [$0] + $0.allSubviews } } }
quelle
Eine Lösung in Swift 3, die alles bietet,
subviews
ohne die Ansicht selbst einzubeziehen:extension UIView { var allSubViews : [UIView] { var array = [self.subviews].flatMap {$0} array.forEach { array.append(contentsOf: $0.allSubViews) } return array } }
quelle
nil
Elemente aus einem Array, um die Sicherheit beim AufrufenallSubViews
von Unteransichten zu gewährleisten.Ich tagge alles, wenn es erstellt wird. Dann ist es einfach, eine Unteransicht zu finden.
quelle
Ich habe 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 (entweder setzen Sie einen Haltepunkt, um ihn manuell anzuhalten), und Sie können nach dem fragen
recursiveDescription
.quelle
Hier ist ein Beispiel mit tatsächlichen Funktionen zum Schleifen und Unterbrechen von Ansichten.
Schnell:
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) } } } }
Beispiel aufrufen:
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 } }
Beispiel aufrufen:
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
Beispiel aufrufen:
[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) { if ([view isKindOfClass:[UIButton class]]) { /// use the view *stop = YES; } }];
quelle
Mit 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 Kategorie "HierarchyLogging" 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];
quelle
Es muss keine neue Funktion erstellt werden. Tun Sie es einfach beim Debuggen mit Xcode.
Legen Sie einen Haltepunkt in einem Ansichts-Controller fest und lassen Sie die App an diesem Haltepunkt anhalten.
Klicken Sie mit der rechten Maustaste auf den leeren Bereich und klicken Sie im Xcode-Überwachungsfenster 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 von ... drucken". Sie sehen alle Unteransichten von self.view im Konsolenfenster. Ändern Sie die Ansicht self -> _ in eine andere Ansicht, wenn Sie keine Unteransichten von self.view anzeigen möchten.
Erledigt! Kein gdb!
quelle
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]; } }
quelle
Ü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]; }]; }
quelle
Hier ist eine Variation der obigen Antwort von Ole Begemann , 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
quelle
Der in dieser Antwort veröffentlichte Code durchläuft alle Fenster und alle Ansichten sowie alle ihre Unteransichten. Es wurde verwendet, um einen Ausdruck der Ansichtshierarchie an NSLog zu senden, aber Sie können ihn als Grundlage für jede Durchquerung der Ansichtshierarchie verwenden. Es verwendet eine rekursive C-Funktion, um den Ansichtsbaum zu durchlaufen.
quelle
Ich habe vor einiger Zeit eine Kategorie geschrieben , um einige Ansichten zu debuggen.
IIRC, der gebuchte Code hat funktioniert. Wenn nicht, zeigt es Ihnen in die richtige Richtung. Verwendung auf eigenes Risiko usw.
quelle
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
quelle
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
quelle
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 abrufen müssen, 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 } }
quelle
Die folgende Methode erstellt ein oder mehrere veränderbare Arrays und durchläuft dann die Unteransichten der Eingabeansicht. Dabei wird die anfängliche Unteransicht hinzugefügt und dann abgefragt, ob Unteransichten dieser Unteransicht vorhanden sind. Wenn true, ruft es sich erneut auf. Dies geschieht so lange, bis alle Ansichten der Hierarchie hinzugefügt wurden.
-(NSArray *)allSubs:(UIView *)view { NSMutableArray * ma = [NSMutableArray new]; for (UIView * sub in view.subviews){ [ma addObject:sub]; if (sub.subviews){ [ma addObjectsFromArray:[self allSubs:sub]]; } } return ma; }
Rufen Sie an mit:
NSArray * subviews = [self allSubs:someView];
quelle