Come posso scorrere tutte le sottoview di un UIView e le loro sottoview e le loro sottoview

Come posso scorrere tutte le sottoview di un UIView e le loro sottoview e le loro sottoview?

Usa ricorsione:

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

Bene, ecco la mia soluzione che utilizza la ricorsione e un wrapper (categoria / estensione) per la class 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 

Utilizzo: ora è necessario eseguire il looping di tutte le viste secondarie e manipolarle in base alle esigenze.

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

Una soluzione in Swift 3 che offre tutte le subviews senza includere la vista stessa:

 extension UIView { var allSubViews : [UIView] { var array = [self.subviews].flatMap {$0} array.forEach { array.append(contentsOf: $0.allSubViews) } return array } } 

Ho appena trovato un modo interessante per farlo attraverso il debugger:

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

fa riferimento a questo Apple Technote:

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

Assicurati solo che il tuo debugger sia messo in pausa (imposta un break point di pausa manualmente) e puoi chiedere la recursiveDescription .

Contrassegno tutto quando viene creato. Quindi è facile trovare qualsiasi sottoview.

 view = [aView viewWithTag:tag]; 

Con l’aiuto di Ole Begemann. Ho aggiunto alcune righe per incorporare il concetto di blocco.

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 

Utilizzo della categoria HierarchyLogging nel ViewController. Ora sei libero di ciò che devi fare.

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

Ecco un codice ricorsivo: –

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

A proposito, ho realizzato un progetto open source per aiutare con questo tipo di compito. È davvero semplice e utilizza i blocchi Objective-C 2.0 per eseguire il codice su tutte le viste in una gerarchia.

https://github.com/egold/UIViewRecursion

Esempio:

 -(void)makeAllSubviewsGreen { [self.view runBlockOnAllSubviews:^(UIView *view) { view.backgroundColor = [UIColor greenColor]; }]; } 

Non è necessario creare alcuna nuova funzione. Fallo quando esegui il debug con Xcode.

Imposta un punto di interruzione in un controller di visualizzazione e fai in pausa l’app in questo punto di interruzione.

Fare clic con il pulsante destro del mouse sull’area vuota e premere “Aggiungi espressione …” nella finestra di visualizzazione di Xcode.

Inserisci questa linea:

 (NSString*)[self->_view recursiveDescription] 

Se il valore è troppo lungo, fai clic con il pulsante destro del mouse e seleziona “Stampa descrizione di …”. Vedrai tutte le sottoview di self.view nella finestra della console. Cambia auto -> _ guarda a qualcos’altro se non vuoi vedere le sottoview di self.view.

Fatto! No gdb!

Ecco un esempio con funzionalità di looping e interruzione della vista effettiva.

 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 

Esempio di chiamata:

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

Ecco una variazione sulla risposta di Ole Begemann sopra, che aggiunge indentazione per illustrare la gerarchia:

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

Il codice pubblicato in questa risposta attraversa tutte le windows e tutte le viste e tutte le loro sottoview. È stato utilizzato per scaricare una stampa della gerarchia della vista in NSLog ma è ansible utilizzarla come base per qualsiasi attraversamento della gerarchia della vista. Usa una funzione C ricorsiva per attraversare l’albero della vista.

Ho scritto una categoria qualche tempo fa per eseguire il debug di alcune visualizzazioni.

IIRC, il codice pubblicato è quello che ha funzionato. In caso contrario, ti indicherà la giusta direzione. Utilizzare a proprio rischio, ecc.

Questo mostra anche il livello della gerarchia

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

Vorrei aver trovato questa pagina prima, ma se (per qualche motivo) vuoi farlo in modo non ricorsivo, non in una categoria e con più righe di codice

Penso che tutte le risposte usando la ricorsione (tranne l’opzione debugger) abbiano usato le categorie. Se non hai bisogno / vuoi una categoria, puoi semplicemente usare un metodo di istanza. Ad esempio, se hai bisogno di ottenere una matrice di tutte le etichette nella tua gerarchia di visualizzazione, puoi farlo.

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

Ecco un’altra implementazione Swift:

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

Il metodo seguente crea uno o più array mutabili, quindi esegue il ciclo delle subview della vista di input. In tal modo aggiunge la sottoview iniziale, quindi interroga se esistono sottoview di quella sottoview. Se è vero, si chiama di nuovo. Lo fa fino a quando non sono state aggiunte tutte le viste della gerarchia.

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

Chiama usando:

 NSArray * subviews = [self allSubs:someView];