Quiero enumerar todas las subvistas en un UIViewController
. Lo intenté self.view.subviews
, pero no se enumeran todas las subvistas, por ejemplo, UITableViewCell
no se encuentran las subvistas en el . ¿Alguna idea?
ios
uiview
subview
uiview-hierarchy
user403015
fuente
fuente
Respuestas:
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.
fuente
recursiveDescription
respuesta mucho más simple de @natbro - stackoverflow.com/a/8962824/429521La 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>>
fuente
[_myToolbar recursiveDescription]
en mi código o en otro lugar.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()
fuente
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]; } }
fuente
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) } }
fuente
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
fuente
Si todo lo que desea es una matriz de
UIView
s, 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 } } }
fuente
Yo uso de esta manera:
NSLog(@"%@", [self.view subviews]);
en el UIViewController.
fuente
Detalles
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
fuente
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
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 }
fuente
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 subvistasEditar: esta publicación de blog sobre Easy UIView Debugging puede ayudar potencialmente
fuente
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 queself
significa 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.fuente
Ejemplo simple de Swift:
var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) }
fuente
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
printAllChildrenOfView
para no tomar ningún parámetro o crear un nuevo método.fuente
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()
fuente
Solución más corta
for subview in self.view.subviews { print(subview.dynamicType) }
Resultado
UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide
Notas
fuente
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>>
superview
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
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:
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.fuente
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 }
fuente
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; }
fuente
Lo he hecho en una categoría de
UIView
simplemente 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 :)
fuente
- (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; }
fuente
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) { ....... } } }
fuente