¿Cómo enumerar todas las subvistas en un uiviewcontroller en iOS?

Quiero enumerar todas las subvistas en un UIViewController . Intenté self.view.subviews , pero no todas las subvistas están enumeradas, por ejemplo, las subvistas en UITableViewCell no se encuentran. ¿Alguna idea?

Debe iterar recursivamente las vistas secundarias.

 - (void)listSubviewsOfView:(UIView *)view { // Get the subviews of the view NSArray *subviews = [view subviews]; // Return if there are no subviews if ([subviews count] == 0) return; // COUNT CHECK LINE for (UIView *subview in subviews) { // Do what you want to do with the subview NSLog(@"%@", subview); // List the subviews of subview [self listSubviewsOfView:subview]; } } 

Como comentó @Greg Meletic, puede omitir la LÍNEA COUNT CHECK anterior.

La forma incorporada de xcode / gdb para volcar la jerarquía de vista es útil – recursiveDescription, por http://developer.apple.com/library/ios/#technotes/tn2239/_index.html

Genera una jerarquía de vistas más completa que puede ser útil:

 > po [_myToolbar recursiveDescription] > | > 

Necesita imprimir de forma recursiva, este método también tabs según la profundidad de la vista

 -(void) printAllChildrenOfView:(UIView*) node depth:(int) d { //Tabs are just for formatting NSString *tabs = @""; for (int i = 0; i < d; i++) { tabs = [tabs stringByAppendingFormat:@"\t"]; } NSLog(@"%@%@", tabs, node); d++; //Increment the depth for (UIView *child in node.subviews) { [self printAllChildrenOfView:child depth:d]; } } 

Aquí está la versión rápida

  func listSubviewsOfView(view:UIView){ // Get the subviews of the view var subviews = view.subviews // Return if there are no subviews if subviews.count == 0 { return } for subview : AnyObject in subviews{ // Do what you want to do with the subview println(subview) // List the subviews of subview listSubviewsOfView(subview as UIView) } } 

Llego un poco tarde a la fiesta, pero una solución un poco más general:

 @implementation UIView (childViews) - (NSArray*) allSubviews { __block NSArray* allSubviews = [NSArray arrayWithObject:self]; [self.subviews enumerateObjectsUsingBlock:^( UIView* view, NSUInteger idx, BOOL*stop) { allSubviews = [allSubviews arrayByAddingObjectsFromArray:[view allSubviews]]; }]; return allSubviews; } @end 

Elegante solución recursiva en Swift:

 extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { $0.subviewsRecursive() } } } 

Puede llamar a subviewsRecursive () en cualquier UIView:

 let allSubviews = self.view.subviewsRecursive() 

Yo uso de esta manera:

 NSLog(@"%@", [self.view subviews]); 

en el UIViewController.

Si lo único que quieres es una matriz de UIView s, esta es una solución de un trazador de líneas ( Swift 4+ ):

 extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews } } } 

El motivo por el que no se imprimen las subvistas en una UITableViewCell es porque debe generar todas las subvistas en el nivel superior. Las subvistas de la celda no son las subvistas directas de su vista.

Para obtener las subvistas de UITableViewCell, debe determinar qué subvistas pertenecen a una UITableViewCell (usando isKindOfClass: en su ciclo de impresión y luego recorrer sus subvistas

Editar: esta publicación de blog en Easy UIView Debugging podría ayudar

Escribí una categoría para enumerar todas las vistas que tenía un controlador de vista que se inspiraba en las respuestas publicadas anteriormente.

 @interface UIView (ListSubviewHierarchy) - (NSString *)listOfSubviews; @end @implementation UIView (ListSubviewHierarchy) - (NSInteger)depth { NSInteger depth = 0; if ([self superview]) { deepth = [[self superview] depth] + 1; } return depth; } - (NSString *)listOfSubviews { NSString * indent = @""; NSInteger depth = [self depth]; for (int counter = 0; counter < depth; counter ++) { indent = [indent stringByAppendingString:@" "]; } __block NSString * listOfSubviews = [NSString stringWithFormat:@"\n%@%@", indent, [self description]; if ([self.subviews count] > 0) { [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { UIView * subview = obj; listOfSubviews = [listOfSubviews stringByAppendingFormat:@"%@", [subview listOfSubviews]]; }]; } return listOfSubviews; } @end 

Para enumerar todas las vistas mantenidas por un controlador de vista, solo NSLog("%@",[self listOfSubviews]) , que significa el propio controlador de vista. Aunque no deja de ser eficiente.

Además, puede usar NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]); hacer lo mismo, y creo que es más eficiente que mi implementación.

Ejemplo simple de Swift:

  var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) } 

En mi camino, la categoría o extensión de UIView es mucho mejor que otras y recursivo es el punto clave para obtener todas las subvistas

aprende más:

https://github.com/ZhipingYang/XYDebugView

C objective

 @implementation UIView (Recurrence) - (NSArray *)recurrenceAllSubviews { NSMutableArray  *all = @[].mutableCopy; void (^getSubViewsBlock)(UIView *current) = ^(UIView *current){ [all addObject:current]; for (UIView *sub in current.subviews) { [all addObjectsFromArray:[sub recurrenceAllSubviews]]; } }; getSubViewsBlock(self); return [NSArray arrayWithArray:all]; } @end 

ejemplo

 NSArray *views = [viewController.view recurrenceAllSubviews]; 

Swift 3.1

 extension UIView { func recurrenceAllSubviews() -> [UIView] { var all = [UIView]() func getSubview(view: UIView) { all.append(view) guard view.subviews.count>0 else { return } view.subviews.forEach{ getSubview(view: $0) } } getSubview(view: self) return all } } 

ejemplo

 let views = viewController.view.recurrenceAllSubviews() 

directamente, use la función de secuencia para obtener todas las subvistas

 let viewSequence = sequence(state: [viewController.view]) { (state: inout [UIView] ) -> [UIView]? in guard state.count > 0 else { return nil } defer { state = state.map{ $0.subviews }.flatMap{ $0 } } return state } let views = Array(viewSequence).flatMap{ $0 } 

Detalles

xCode 9.0.1, Swift 4

Solución

 extension UIView { private var viewInfo: String { return "\(classForCoder), frame: \(frame))" } private func subviews(parentView: UIView, level: Int = 0, printSubviews: Bool = false) -> [UIView] { var result = [UIView]() if level == 0 && printSubviews { result.append(parentView) print("\(parentView.viewInfo)") } for subview in parentView.subviews { if printSubviews { print("\(String(repeating: "-", count: level))\(subview.viewInfo)") } result.append(subview) if subview.subviews.count != 0 { result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews) } } return result } var allSubviews: [UIView] { return subviews(parentView: self) } func printSubviews() { _ = subviews(parentView: self, printSubviews: true) } } 

Uso

  view.printSubviews() print("\(view.allSubviews.count)") 

Resultado

enter image description here

Podrías probar un truco de arreglo elegante, como:

[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];

Solo una linea de codigo Por supuesto, puede que necesite ajustar su método printAllChildrenOfView para no tomar ningún parámetro o crear un nuevo método.

Compatible con Swift 2.0

Aquí un método recursivo para obtener todas las subvistas de una vista genérica:

 extension UIView { func subviewsList() -> [UIView] { var subviews = self.subviews if subviews.count == 0 { return subviews + [] } for v in subviews { subviews += v.listSubviewsOfView() } return subviews } } 

Para que pueda llamar a todas partes de esta manera:

 let view = FooController.view let subviews = view.subviewsList() 

Esto es una reescritura de esta respuesta:

Primero debe obtener el puntero / referencia al objeto que desea imprimir todas sus subvistas. A veces puede encontrar más fácil encontrar ese objeto accediéndolo a través de su subvista. Me gusta po someSubview.superview . Esto te dará algo como:

 Optional ▿ some : > 
  • FaceBookApp es el nombre de tu aplicación
  • WhatsNewView es el tipo de su superview
  • 0x7f91747c71f0 es el puntero a la supervista.

Para imprimir la supervista, debe usar puntos de interrupción.


Ahora para hacer este paso, puede hacer clic en “ver jerarquía de depuración”. No hay necesidad de puntos de interrupción

enter image description here

Entonces podrías hacer fácilmente:

 po [0x7f91747c71f0 recursiveDescription] 

que para mí me devolvió algo así como:

 > | > | | > | | > | | | ; layer = <_uilabellayer: 0x610000085550>> | | | ; layer = <_uilabellayer: 0x610000087300>> | ; layer = ; contentOffset: {0, 0}; contentSize: {264, 890}> | | <<_uitextcontainerview: 0x7f9170a13350; frame = (0 0; 264 890); layer = <_UITextTiledLayer: 0x6080002c0930>> minSize = {0, 0}, maxSize = {1.7976931348623157e+308, 1.7976931348623157e+308}, textContainer = ; exclusionPaths = 0x61000001bc30; lineBreakMode = 0> | | | <_uitilelayer: 0x60800023f8a0> (layer) | | | <_uitilelayer: 0x60800023f3c0> (layer) | | | <_uitilelayer: 0x60800023f360> (layer) | | | <_uitilelayer: 0x60800023eca0> (layer) | | > | | > | > | > | | > 

como debes haber adivinado, mi supervista tiene 4 subvistas:

  • un stackView (el stackView tiene una imagen y otro stackView (este stackView tiene 2 tags personalizadas ))
  • un textView
  • una vista
  • un botón

Esto es bastante nuevo para mí, pero me ha ayudado a depurar los marcos (y el texto y el tipo) de mis vistas. Una de mis subvistas no aparecía en la pantalla, así que utilicé RecursiveDescription y me di cuenta de que el ancho de mi uno de mis subView era 0 … así que fui corregido sus restricciones y apareció la subvista.

Alternativamente, si desea devolver una matriz de todas las subvistas (y subvistas anidadas) desde una extensión UIView:

 func getAllSubviewsRecursively() -> [AnyObject] { var allSubviews: [AnyObject] = [] for subview in self.subviews { if let subview = subview as? UIView { allSubviews.append(subview) allSubviews = allSubviews + subview.getAllSubviewsRecursively() } } return allSubviews } 

enter image description here

La solución más corta

 for subview in self.view.subviews { print(subview.dynamicType) } 

Resultado

 UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide 

Notas

  • Como puede ver, este método no enumera las subvistas recursivamente. Vea algunas de las otras respuestas para eso.

Versión de AC # Xamarin:

 void ListSubviewsOfView(UIView view) { var subviews = view.Subviews; if (subviews.Length == 0) return; foreach (var subView in subviews) { Console.WriteLine("Subview of type {0}", subView.GetType()); ListSubviewsOfView(subView); } } 

Alternativamente, si quiere encontrar todas las subvistas de un tipo específico, yo uso:

 List FindViews(UIView view) { List allSubviews = new List(); var subviews = view.Subviews.Where(x => x.GetType() == typeof(T)).ToList(); if (subviews.Count == 0) return allSubviews; foreach (var subView in subviews) { allSubviews.AddRange(FindViews(subView)); } return allSubviews; } 

Lo he hecho en una categoría de UIView simplemente llame a la función que pasa el índice para imprimirlos con un buen formato de árbol. Esta es solo otra opción de la respuesta publicada por James Webster .

 #pragma mark - Views Tree - (void)printSubviewsTreeWithIndex:(NSInteger)index { if (!self) { return; } NSString *tabSpace = @""; @autoreleasepool { for (NSInteger x = 0; x < index; x++) { tabSpace = [tabSpace stringByAppendingString:@"\t"]; } } NSLog(@"%@%@", tabSpace, self); if (!self.subviews) { return; } @autoreleasepool { for (UIView *subView in self.subviews) { [subView printViewsTreeWithIndex:index++]; } } } 

Espero que ayude 🙂

 - (NSString *)recusiveDescription:(UIView *)view { NSString *s = @""; NSArray *subviews = [view subviews]; if ([subviews count] == 0) return @"no subviews"; for (UIView *subView in subviews) { s = [NSString stringWithFormat:@"<%@; frame = (%f %f : %f %f) \n ",NSStringFromClass([subView class]), subView.frame.origin.x, subView.frame.origin.y ,subView.frame.size.width, subView.frame.size.height]; [self recusiveDescription:subView]; } return s; } 

self.view.subviews mantiene la jerarquía de vistas. Para obtener subvistas de uitableviewcell debes hacer algo como a continuación.

  for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }