¿Cómo enumerar todas las subvistas en un controlador de uiview en iOS?


86

Quiero enumerar todas las subvistas en un UIViewController. Lo intenté self.view.subviews, pero no se enumeran todas las subvistas, por ejemplo, UITableViewCellno se encuentran las subvistas en el . ¿Alguna idea?


Puede encontrar todas las subvistas mediante búsqueda recursiva. es decir, verifique que la subvista tiene subvistas ..
Mahesh

Respuestas:


171

Tienes que iterar recursivamente las sub vistas.

- (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 lo comentó @Greg Meletic, puede omitir la LÍNEA DE COMPROBACIÓN DE CONTAR arriba.


20
Técnicamente, no es necesario comprobar si el recuento de subvistas es 0.
Greg Maletic

5
NSLog (@ "\ n% @", [(id) self.view performSelector: @selector (recursiveDescription)]); ¡Esta línea imprime el mismo resultado que el tuyo!
Hemang

Si está aquí, POR FAVOR verifique la recursiveDescriptionrespuesta mucho más simple de @natbro - stackoverflow.com/a/8962824/429521
Felipe Sabino

Aquí está mi mejora a la solución @EmptyStack. Muestra el nombre de la clase y las coordenadas del marco con sangría recursiva
Pierre de LESPINAY

35

La forma integrada de xcode / gdb para volcar la jerarquía de vistas es útil: recursiveDescription, según http://developer.apple.com/library/ios/#technotes/tn2239/_index.html

Genera una jerarquía de vista más completa que puede resultarle útil:

> po [_myToolbar recursiveDescription]

<UIToolbarButton: 0xd866040; frame = (152 0; 15 44); opaque = NO; layer = <CALayer: 0xd864230>>
   | <UISwappableImageView: 0xd8660f0; frame = (0 0; 0 0); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xd86a160>>

Dónde poner el po [_myToolbar recursiveDescription]en mi código o en otro lugar.
पवन

1
@WildPointer Está hablando de la consola. Necesita un punto de interrupción en algún lugar para hacer esto. po = objeto de impresión
smileBot

1
+1 Apple recomienda este enfoque (según la sesión de Hidden Gems in Cocoa and Cocoa Touch de WWDC 2013, consejo n. ° 5).
Slipp D. Thompson

@ पवन vea mi respuesta aquí . Hice una reescritura de esta respuesta.
Miel

16

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

13

Necesita imprimir de forma recursiva, este método también pestañas 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];
    }

}

13

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

7

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

6

Si todo lo que desea es una matriz de UIViews, esta es una solución de una sola línea ( Swift 4+ ):

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

gracias por esta solucion! Pasé horas averiguando, ¡pero luego tu publicación me salvó!
user2525211

5

Yo uso de esta manera:

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

en el UIViewController.


4

Detalles

  • Xcode 9.0.1, Swift 4
  • Xcode 10.2 (10E125), Swift 5

Solución

extension UIView {
    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.isEmpty { continue }
            result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews)
        }
        return result
    }
    private var viewInfo: String { return "\(classForCoder), frame: \(frame))" }
    var allSubviews: [UIView] { return subviews(parentView: self) }
    func printSubviews() { _ = subviews(parentView: self, printSubviews: true) }
}

Uso

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

Resultado

ingrese la descripción de la imagen aquí


3

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

aprende más:

https://github.com/ZhipingYang/XYDebugView

ingrese la descripción de la imagen aquí

C objetivo

@implementation UIView (Recurrence)

- (NSArray<UIView *> *)recurrenceAllSubviews
{
    NSMutableArray <UIView *> *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 = viewSequence.flatMap{ $0 }

2

La razón por la que las subvistas en un UITableViewCell no se imprimen 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 UITableViewCell (usando isKindOfClass:) en su ciclo de impresión y luego recorrer sus subvistas

Editar: esta publicación de blog sobre Easy UIView Debugging puede ayudar potencialmente


2

Escribí una categoría para enumerar todas las vistas en poder de un controlador de vista que se inspiró 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 que tiene un controlador de vista, simplemente NSLog("%@",[self listOfSubviews]), lo que selfsignifica el controlador de vista en sí. Aunque no es muy eficiente.

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


2

Ejemplo simple de Swift:

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

1

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

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

Solo una línea de código. Por supuesto, es posible que deba ajustar su método printAllChildrenOfViewpara no tomar ningún parámetro o crear un nuevo método.


1

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

1

ingrese la descripción de la imagen aquí

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 de forma recursiva. Vea algunas de las otras respuestas para eso.

1

Esta es una reescritura de esta respuesta:

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

Optional<UIView>
   some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp es el nombre de su aplicación
  • WhatsNewView es el tipo de susuperview
  • 0x7f91747c71f0 es el puntero a la supervista.

Para imprimir el superView, debe utilizar puntos de interrupción.


Ahora, para realizar este paso, puede hacer clic en "ver jerarquía de depuración". Sin necesidad de puntos de interrupción

ingrese la descripción de la imagen aquí

Entonces podrías hacer fácilmente:

po [0x7f91747c71f0 recursiveDescription]

que para mi devolvió algo como:

<FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
   | <UIStackView: 0x7f91747c75f0; frame = (45 60; 264 93); layer = <CATransformLayer: 0x610000230ec0>>
   |    | <UIImageView: 0x7f916ef38c30; frame = (10.6667 0; 243 58); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x61000003b840>>
   |    | <UIStackView: 0x7f91747c8230; frame = (44.6667 58; 174.667 35); layer = <CATransformLayer: 0x6100006278c0>>
   |    |    | <FacebookApp.CopyableUILabel: 0x7f91747a80b0; baseClass = UILabel; frame = (44 0; 86.6667 16); text = 'What's New'; gestureRecognizers = <NSArray: 0x610000c4a770>; layer = <_UILabelLayer: 0x610000085550>>
   |    |    | <FacebookApp.CopyableUILabel: 0x7f916ef396a0; baseClass = UILabel; frame = (0 21; 174.667 14); text = 'Version 14.0.5c Oct 05, 2...'; gestureRecognizers = <NSArray: 0x610000c498a0>; layer = <_UILabelLayer: 0x610000087300>>
   | <UITextView: 0x7f917015ce00; frame = (45 183; 264 403); text = '   •   new Adding new feature...'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x6100000538f0>; layer = <CALayer: 0x61000042f000>; 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 = <NSTextContainer: 0x610000117b20 size = (264.000000,340282346638528859811704183484516925440.000000); widthTracksTextView = YES; heightTracksTextView = NO>; exclusionPaths = 0x61000001bc30; lineBreakMode = 0>
   |    |    | <_UITileLayer: 0x60800023f8a0> (layer)
   |    |    | <_UITileLayer: 0x60800023f3c0> (layer)
   |    |    | <_UITileLayer: 0x60800023f360> (layer)
   |    |    | <_UITileLayer: 0x60800023eca0> (layer)
   |    | <UIImageView: 0x7f9170a7d370; frame = (-39 397.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f4c0>>
   |    | <UIImageView: 0x7f9170a7d560; frame = (258.667 -39; 2.33333 36); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f5e0>>
   | <UIView: 0x7f916ef149c0; frame = (0 587; 354 0); layer = <CALayer: 0x6100006392a0>>
   | <UIButton: 0x7f91747a8730; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x610000639320>>
   |    | <UIButtonLabel: 0x7f916ef00a80; frame = (0 -5.66667; 0 16); text = 'See More Details'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x610000084d80>>

como habrás adivinado, mi supervista tiene 4 subvistas:

  • un stackView (el stackView en sí tiene una imagen y otro stackView (este stackView tiene 2 etiquetas personalizadas ))
  • un textView
  • una vista
  • un botón

Esto es bastante nuevo para mí, pero me ha ayudado a depurar los marcos de mis vistas (y texto y tipo). Una de mis subvistas no aparecía en la pantalla, así que utilicé recursiveDescription y me di cuenta de que el ancho de una de mis subvistas era 0... así que corrigí sus restricciones y la subvista estaba apareciendo.


0

Alternativamente, si desea devolver una matriz de todas las subvistas (y subvistas anidadas) de 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
}

0

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 desea encontrar todas las subvistas de un tipo específico, uso:

List<T> FindViews<T>(UIView view)
{
    List<T> allSubviews = new List<T>();
    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<T>(subView));
        }

    return allSubviews;

}

0

Lo he hecho en una categoría de UIViewsimplemente llamar a la función pasando el índice para imprimirlos con un bonito 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 :)


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

-1

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

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.