Wie kann ich alle Unteransichten einer UIView sowie deren Unteransichten und Unteransichten durchlaufen?
Wie kann ich alle Unteransichten einer UIView sowie deren Unteransichten und Unteransichten durchlaufen?
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];
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;
}
}
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 }
}
}
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
}
}
nil
Elemente aus einem Array, um die Sicherheit beim Aufrufen allSubViews
von Unteransichten zu gewährleisten.
Ich tagge alles, wenn es erstellt wird. Dann ist es einfach, eine Unteransicht zu finden.
view = [aView viewWithTag:tag];
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
.
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;
}
}];
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];
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!
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];
}
}
Ü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];
}];
}
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
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 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.
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.
@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
}
}
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];