如何列出iOS中的uiviewcontroller中的所有子视图?

我想列出UIViewController所有子视图。 我尝试了self.view.subviews ,但并不是所有的子视图都列出来了,例如UITableViewCell中的子视图找不到。 任何想法?

你必须recursion迭代子视图。

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

正如@Greg Meletic所评论的,您可以跳过上面的COUNT CHECK LINE。

转储视图层次结构的xcode / gdb内置方式非常有用 – recursion描述,根据http://developer.apple.com/library/ios/#technotes/tn2239/_index.html

它输出一个更完整的视图层次结构,您可能会发现有用的:

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

您需要recursion打印,此方法也基于视图的深度制表符

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

这是快速的版本

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

我晚了一点,但有点更一般的解决scheme:

 @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 

我用这种方式:

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

在UIViewController中。

UITableViewCell中的子视图不打印的原因是因为您必须输出顶层的所有子视图。 单元格的子视图不是视图的直接子视图。

为了获得UITableViewCell的子视图,需要确定哪些子视图属于打印循环中的UITableViewCell(使用isKindOfClass: ,然后遍历它的子视图

编辑: Easy UIViewdebugging这个博客文章可能有帮助

我写了一个类别来列出视图控制器所持有的所有视图,这个视图控制器受到之前发布的答案的启发

 @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 

要列出由视图控制器持有的所有视图,只需NSLog("%@",[self listOfSubviews]) ,其self意味着视图控制器本身。 虽然没有退出效率。

另外,你可以使用NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]); 做同样的事情,我认为这比我的实施更有效率。

简单的Swift例子:

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

你可以尝试一个奇特的数组技巧,如:

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

只需一行代码。 当然,你可能需要调整你的方法printAllChildrenOfView来不带任何参数或者创build一个新的方法。

Swift 2.0兼容

这里有一个recursion的方法来获取一个通用视图的所有子视图:

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

所以你可以用这种方式到处打电话:

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

我对于迄今为止提供的答案并不完全满意,所以我推出了自己的产品,我认为这是一个很好的select:

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

然后你可以在任何UIView上调用subviewsRecursive():

 let allSubviews = self.view.subviewsRecursive() 

或者,如果你想从一个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 } 

在这里输入图像描述

最短的解决scheme

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

结果

 UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide 

笔记

  • 正如你所看到的,这个方法不会recursion地列出子视图。 看到一些其他的答案。

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

另外,如果你想find所有我使用的特定types的子视图:

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

我已经在一个UIView的类别中完成,只是调用传递索引的函数以良好的树格式打印它们。 这只是James Webster发表的答案的另一种select。

 #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++]; } } } 

我希望它有助于:)

以我的方式,UIView的类别或扩展比其他更好,recursion是获得所有子视图的关键点

学到更多:

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 

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

 let views = viewController.view.recurrenceAllSubviews() 

直接使用序列函数来获取所有子视图

 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 } 

细节

xCode 9.0.1,Swift 4

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

用法

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

结果

在这里输入图像描述

这个重写这个答案:

您必须首先获取您打算打印所有子视图的对象的指针/引用。 有时你可能会发现通过子视图访问它可以更容易地find该对象。 像po someSubview.superview 。 这会给你类似的东西:

 Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>> 
  • FaceBookApp是你的应用程序名称
  • WhatsNewView是你的超级superviewtypes
  • 0x7f91747c71f0是指向0x7f91747c71f0的指针。

要打印superView,您必须使用断点。


现在做这一步,你可以点击“查看debugging层次结构”。 不需要断点

在这里输入图像描述

那么你可以轻松做到:

 po [0x7f91747c71f0 recursiveDescription] 

这对我来说是这样的:

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

因为你一定已经猜到我的超级观点有4个子视图:

  • 一个stackView(stackView本身有一个图像和另一个stackView(这个stackView有2个自定义标签))
  • 一个textView
  • 一个看法
  • 一个button

这对我来说相当新颖,但帮助我debugging了我的视图框架(以及文本和types)。 我的一个子视图没有显示在屏幕上,所以使用了recursion描述,我意识到我的一个子视图的宽度是0 …所以我去纠正其约束和子视图出现。

self.view.subviews维护视图的层次。要获得uitableviewcell的子视图,你必须做下面的事情。

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