Eu quero listar todas as subvisualizações em a UIViewController
. Eu tentei self.view.subviews
, mas nem todas as subvisualizações estão listadas, por exemplo, as subvisualizações no UITableViewCell
não foram encontradas. Qualquer ideia?
ios
uiview
subview
uiview-hierarchy
user403015
fonte
fonte
Respostas:
Você precisa iterar recursivamente as subvisões.
- (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]; } }
Conforme comentado por @Greg Meletic, você pode pular a LINHA DE VERIFICAÇÃO DE CONTAGEM acima.
fonte
recursiveDescription
resposta muito mais simples de @natbro - stackoverflow.com/a/8962824/429521A forma integrada xcode / gdb de despejar a hierarquia de visualização é útil - recursiveDescription, de acordo com http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
Ele produz uma hierarquia de visualização mais completa que pode ser ú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>>
fonte
[_myToolbar recursiveDescription]
no meu código ou em outro lugar.Solução recursiva elegante em Swift:
extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { $0.subviewsRecursive() } } }
Você pode chamar subviewsRecursive () em qualquer UIView:
let allSubviews = self.view.subviewsRecursive()
fonte
Você precisa imprimir recursivamente, este método também abas com base na profundidade da 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]; } }
fonte
Aqui está a versão 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) } }
fonte
Estou um pouco atrasado para a festa, mas uma solução um pouco mais geral:
@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
fonte
Se tudo o que você deseja é uma matriz de
UIView
s, esta é uma solução de um revestimento ( Swift 4+ ):extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews } } }
fonte
Eu uso desta forma:
NSLog(@"%@", [self.view subviews]);
no UIViewController.
fonte
Detalhes
Solução
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
fonte
A meu ver, a categoria ou extensão do UIView é muito melhor do que outras e recursiva é o ponto chave para obter todas as subvisualizações
Saber mais:
https://github.com/ZhipingYang/XYDebugView
Objective-C
@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
exemplo
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 } }
exemplo
let views = viewController.view.recurrenceAllSubviews()
diretamente, use a função de sequência para obter todas as subvisualizações
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 }
fonte
A razão pela qual as subvisualizações em um UITableViewCell não são impressas é porque você deve enviar todas as subvisualizações no nível superior. As subvisualizações da célula não são as subvisualizações diretas da sua visão.
A fim de obter as subvisualizações de UITableViewCell, você precisa determinar quais subvisualizações pertencem a um UITableViewCell (usando
isKindOfClass:
) em seu loop de impressão e, em seguida, percorrer suas subvisualizaçõesEdit: esta postagem do blog sobre Easy UIView Debugging pode potencialmente ajudar
fonte
Eu escrevi uma categoria para listar todas as visualizações mantidas por um controlador de visualização inspirado nas respostas postadas antes.
@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 listar todas as visualizações mantidas por um controlador de visualização, apenas
NSLog("%@",[self listOfSubviews])
, o queself
significa o próprio controlador de visualização. Embora não seja muito eficiente.Além disso, você pode usar
NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);
para fazer a mesma coisa, e acho que é mais eficiente do que minha implementação.fonte
Exemplo simples de Swift:
var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) }
fonte
Você pode tentar um truque de array sofisticado, como:
[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];
Apenas uma linha de código. Claro, você pode precisar ajustar seu método
printAllChildrenOfView
para não tomar nenhum parâmetro ou fazer um novo método.fonte
Compatível com Swift 2.0
Aqui, um método recursivo para obter todas as subvisualizações de uma visão 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 } }
Então você pode ligar para qualquer lugar desta forma:
let view = FooController.view let subviews = view.subviewsList()
fonte
Solução mais curta
for subview in self.view.subviews { print(subview.dynamicType) }
Resultado
UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide
Notas
fonte
Isso é uma reescrita desta resposta:
Você deve primeiro obter o ponteiro / referência para o objeto que pretende imprimir todas as suas subvisualizações. Às vezes, você pode achar mais fácil encontrar esse objeto acessando-o por meio de sua subvisão. Gosto
po someSubview.superview
. Isso lhe dará algo como:Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
superview
0x7f91747c71f0
é o ponteiro para a supervisualização.Para imprimir o superView, você deve usar pontos de interrupção.
Agora, para fazer esta etapa, você pode apenas clicar em 'ver hierarquia de depuração'. Não há necessidade de pontos de interrupção
Então você poderia facilmente fazer:
po [0x7f91747c71f0 recursiveDescription]
que para mim retornou 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 você deve ter adivinhado, meu superview tem 4 subviews:
Isso é bastante novo para mim, mas me ajudou a depurar os quadros de minhas visualizações (e texto e tipo). Uma das minhas subvisualizações não estava aparecendo na tela, então usei recursiveDescription e percebi que a largura da minha uma das subvisualizações era
0
... então fui corrigido suas restrições e a subvisualização estava aparecendo.fonte
Como alternativa, se você quiser retornar uma matriz de todas as subvisualizações (e subvisualizações aninhadas) de uma extensão 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 }
fonte
Versão 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, se você quiser encontrar todas as subvisualizações de um tipo específico que eu 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; }
fonte
Eu fiz isso em uma categoria de
UIView
apenas chamar a função passando o índice para imprimi-los com um formato de árvore agradável. Esta é apenas mais uma opção da resposta postada 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 ajude :)
fonte
- (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; }
fonte
self.view.subviews mantém a hierarquia de visualizações. Para obter subvisualizações de uitableviewcell, você deve fazer algo como a seguir.
for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }
fonte