¿Hay alguna forma de pasar una función en logViewHierarchy? De esa manera, puede adaptarse a sus necesidades en diferentes momentos.
docchang
2
@docchang: Los bloques Obj-C son ideales para ese caso de uso. Pasas un bloque al método, ejecutas el bloque y lo pasas por la jerarquía con cada llamada al método.
Ole Begemann
Agradable. Publiqué un fragmento a continuación que agrega sangría de espacios en blanco a esta técnica.
jaredsinclair
34
Bueno, aquí está mi solución usando recursividad y un contenedor (categoría / extensión) para la clase UIView.
Tenga en cuenta que hay pérdida de memoria en la allSubViewsfunción: debe crear una matriz como [[[NSMutableArray alloc] init] autorelease]o como [NSMutableArray array](que es lo mismo).
ivanzoid
1
Por "un contenedor para UIView", en realidad se refiere a "una categoría para UIView".
Solo asegúrese de que su depurador esté en pausa (establezca un punto de interrupción o pause manualmente) y puede solicitar el archivo recursiveDescription.
Parece que hice una solución muy similar a la que propusiste aquí. Seguí adelante y lo publiqué en github en caso de que otros pudieran encontrarlo útil. Aquí está mi respuesta con el enlace :) stackoverflow.com/a/10440510/553394
Eric Goldberg
¿Cómo podría agregar una forma de detener la recursividad desde dentro del bloque? Supongamos que estoy usando esto para ubicar una vista específica, una vez que la encuentre, me gustaría detenerme allí y no continuar buscando en el resto de la jerarquía de vistas.
Mic Pringle
4
No es necesario crear ninguna función nueva. Solo hazlo cuando depures con Xcode.
Establezca un punto de interrupción en un controlador de vista y haga que la aplicación se detenga en este punto de interrupción.
Haz clic derecho en el área vacía y presiona "Agregar expresión ..." en la ventana Watch de Xcode.
Ingrese esta línea:
(NSString*)[self->_view recursiveDescription]
Si el valor es demasiado largo, haga clic derecho y seleccione "Imprimir descripción de ...". Verá todas las subvistas de self.view en la ventana de la consola. Cambie self -> _ view a otra cosa si no desea ver subvistas de self.view.
solución útil y ahorro de tiempo .. gracias 1 más para usted
Brainstorm Technolabs
3
Por cierto, hice un proyecto de código abierto para ayudar con este tipo de tarea. Es realmente fácil y usa bloques Objective-C 2.0 para ejecutar código en todas las vistas en una jerarquía.
El código publicado en esta respuesta atraviesa todas las ventanas y todas las vistas y todas sus subvistas. Se usó para volcar una impresión de la jerarquía de vistas en NSLog, pero puede usarlo como base para cualquier recorrido de la jerarquía de vistas. Utiliza una función C recursiva para recorrer el árbol de vistas.
Ojalá hubiera encontrado esta página primero, pero si (por alguna razón) desea hacer esto de forma no recursiva, no en una categoría y con más líneas de código
Creo que todas las respuestas que usan la recursividad (excepto la opción del depurador) usan categorías. Si no necesita / desea una categoría, puede usar un método de instancia. Por ejemplo, si necesita obtener una matriz de todas las etiquetas en su jerarquía de vista, puede hacer esto.
El método siguiente crea una o más matrices mutables, luego recorre las subvistas de la vista de entrada. Al hacerlo, agrega la subvista inicial y luego consulta si hay alguna subvista de esa subvista. Si es cierto, se vuelve a llamar a sí mismo. Lo hace hasta que se hayan agregado todas las vistas de la jerarquía.
-(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;
}
Utilice el enlace de edición para explicar cómo funciona este código y no solo dé el código, ya que es más probable que una explicación ayude a los futuros lectores. Consulte también Cómo responder . fuente
Respuestas:
Usar recursividad:
// 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];
fuente
Bueno, aquí está mi solución usando recursividad y un contenedor (categoría / extensión) para la clase 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
Uso: Ahora debería recorrer todas las sub vistas y manipularlas según sea necesario.
//disable all text fields for(UIView *v in [self.view allSubViews]) { if([v isKindOfClass:[UITextField class]]) { ((UITextField*)v).enabled=NO; } }
fuente
allSubViews
función: debe crear una matriz como[[[NSMutableArray alloc] init] autorelease]
o como[NSMutableArray array]
(que es lo mismo).Aquí hay otra implementación de Swift:
extension UIView { var allSubviews: [UIView] { return self.subviews.flatMap { [$0] + $0.allSubviews } } }
fuente
Una solución en Swift 3 que lo da todo
subviews
sin incluir la vista en sí:extension UIView { var allSubViews : [UIView] { var array = [self.subviews].flatMap {$0} array.forEach { array.append(contentsOf: $0.allSubViews) } return array } }
fuente
nil
elementos de una matriz, por seguridad al llamarallSubViews
a subvistas.Etiqueto todo cuando se crea. Entonces es fácil encontrar cualquier subvista.
fuente
Acabo de encontrar una forma interesante de hacer esto a través del depurador:
http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/
hace referencia a esta nota técnica de Apple:
https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT
Solo asegúrese de que su depurador esté en pausa (establezca un punto de interrupción o pause manualmente) y puede solicitar el archivo
recursiveDescription
.fuente
A continuación se muestra un ejemplo con la funcionalidad de ruptura y bucle de vista real.
Rápido:
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) } } } }
Ejemplo de llamada:
mainView.loopViewHierarchy { (view, stop) in if view is UIButton { /// use the view stop = true } }
Bucle invertido:
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 } }
Ejemplo de llamada:
mainView.loopViewHierarchyReversed { (view, stop) in if view is UIButton { /// use the view stop = true } }
C objetivo:
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
Ejemplo de llamada:
[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) { if ([view isKindOfClass:[UIButton class]]) { /// use the view *stop = YES; } }];
fuente
Con la ayuda de Ole Begemann. Agregué algunas líneas para incorporar el concepto de bloque.
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
Usando la categoría HierarchyLogging en su ViewController. Ahora tiene libertad para lo que necesita hacer.
void (^ViewActionBlock)(UIView *) = ^(UIView *view) { if ([view isKindOfClass:[UIButton class]]) { NSLog(@"%@", view); } }; [self.view logViewHierarchy: ViewActionBlock];
fuente
No es necesario crear ninguna función nueva. Solo hazlo cuando depures con Xcode.
Establezca un punto de interrupción en un controlador de vista y haga que la aplicación se detenga en este punto de interrupción.
Haz clic derecho en el área vacía y presiona "Agregar expresión ..." en la ventana Watch de Xcode.
Ingrese esta línea:
(NSString*)[self->_view recursiveDescription]
Si el valor es demasiado largo, haga clic derecho y seleccione "Imprimir descripción de ...". Verá todas las subvistas de self.view en la ventana de la consola. Cambie self -> _ view a otra cosa si no desea ver subvistas de self.view.
¡Hecho! ¡No gdb!
fuente
Aquí hay un código recursivo: -
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]; } }
fuente
Por cierto, hice un proyecto de código abierto para ayudar con este tipo de tarea. Es realmente fácil y usa bloques Objective-C 2.0 para ejecutar código en todas las vistas en una jerarquía.
https://github.com/egold/UIViewRecursion
Ejemplo:
-(void)makeAllSubviewsGreen { [self.view runBlockOnAllSubviews:^(UIView *view) { view.backgroundColor = [UIColor greenColor]; }]; }
fuente
Aquí hay una variación de la respuesta de Ole Begemann anterior, que agrega sangría para ilustrar la jerarquía:
// 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
fuente
El código publicado en esta respuesta atraviesa todas las ventanas y todas las vistas y todas sus subvistas. Se usó para volcar una impresión de la jerarquía de vistas en NSLog, pero puede usarlo como base para cualquier recorrido de la jerarquía de vistas. Utiliza una función C recursiva para recorrer el árbol de vistas.
fuente
Escribí una categoría algún tiempo para depurar algunas vistas.
IIRC, el código publicado es el que funcionó. Si no, le indicará la dirección correcta. Usar bajo su propio riesgo, etc.
fuente
Esto también muestra el nivel de jerarquía
@implementation UIView (ViewHierarchyLogging) - (void)logViewHierarchy:(int)level { NSLog(@"%d - %@", level, self); for (UIView *subview in self.subviews) { [subview logViewHierarchy:(level+1)]; } } @end
fuente
Ojalá hubiera encontrado esta página primero, pero si (por alguna razón) desea hacer esto de forma no recursiva, no en una categoría y con más líneas de código
fuente
Creo que todas las respuestas que usan la recursividad (excepto la opción del depurador) usan categorías. Si no necesita / desea una categoría, puede usar un método de instancia. Por ejemplo, si necesita obtener una matriz de todas las etiquetas en su jerarquía de vista, puede hacer esto.
@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 } }
fuente
El método siguiente crea una o más matrices mutables, luego recorre las subvistas de la vista de entrada. Al hacerlo, agrega la subvista inicial y luego consulta si hay alguna subvista de esa subvista. Si es cierto, se vuelve a llamar a sí mismo. Lo hace hasta que se hayan agregado todas las vistas de la jerarquía.
-(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; }
Llamar usando:
NSArray * subviews = [self allSubs:someView];
fuente