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


86

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


您可以通过递归搜索找到所有子视图。即检查子视图有子视图。–
Mahesh

Answers:


171

您必须递归地迭代子视图。

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


20
从技术上讲,你并不需要检查,如果子视图数为0
格雷格Maletic

5
NSLog(@“ \ n%@”,[(id)self.view performSelector:@selector(recursiveDescription)]);; 该行打印出与您相同的结果!
Hemang 2014年

如果你在这里,请检查要简单得多recursiveDescription,从@natbro答案- stackoverflow.com/a/8962824/429521
菲利普·萨比诺

这是我对@EmptyStack解决方案的改进。它显示了类名和框架坐标的递归缩进
Pierre de LESPINAY,2014年

35

转储视图层次结构的xcode / gdb内置方法很有用-recursiveDescription,根据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>>

[_myToolbar recursiveDescription]在我的代码中或其他地方放置po的位置。
2012年

1
@WildPointer他在谈论控制台。您需要在某处设置断点来执行此操作。po =打印对象
smileBot 2013年

1
+1苹果公司推荐这种方法(根据WWDC 2013的可可粉和可可粉触摸中的隐藏宝石,提示#5)。
Slipp D. Thompson

@पवन在这里看到我的答案。我重写了这个答案。
蜂蜜

16

Swift中的优雅递归解决方案:

extension UIView {

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

}

您可以在任何UIView上调用subviewsRecursive():

let allSubviews = self.view.subviewsRecursive()

13

您需要递归打印,该方法还会根据视图的深度进行制表

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

这是快速版本

 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

我参加聚会有点晚,但是更通用的解决方案是:

@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

如果您只需要一个UIViews数组,则这是一个线性解决方案(Swift 4+):

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

感谢您的解决方案!我花了数小时来弄清楚,但是您的帖子救了我!
user2525211'9

5

我用这种方式:

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

在UIViewController中。


4

细节

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

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

用法

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

结果

在此处输入图片说明


3

以我的方式,UIView的类别或扩展比其他类别好得多,而递归是获取所有子视图的关键

学到更多:

https://github.com/ZhipingYang/XYDebugView

在此处输入图片说明

目标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];

斯威夫特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 = viewSequence.flatMap{ $0 }

2

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

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

编辑:Easy UIView Debugging上的此博客文章可能会有所帮助


2

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

@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)]);用来做同样的事情,我认为它比我的实现更有效。


2

简单的Swift示例:

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

1

您可以尝试使用奇特的数组技巧,例如:

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

只需一行代码。当然,您可能需要调整方法,printAllChildrenOfView使其不接受任何参数或制作新方法。


1

兼容Swift 2.0

这里是获取通用视图的所有子视图的递归方法:

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

1

在此处输入图片说明

最短的解决方案

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

结果

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

笔记

  • 如您所见,此方法不会递归列出子视图。参见其他一些答案。

1

这是对此答案的重写:

您必须首先获取要打印其所有子视图的对象的指针/引用。有时,您可能会发现通过其子视图访问该对象更容易找到该对象。喜欢po someSubview.superview。这将为您提供以下信息:

Optional<UIView>
   some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp是您的应用名称
  • WhatsNewView是您的类型superview
  • 0x7f91747c71f0 是指向超级视图的指针。

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


现在执行此步骤,您只需单击“查看调试层次结构”即可。无需断点

在此处输入图片说明

然后,您可以轻松地执行以下操作:

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
  • 一个看法
  • 一个按钮

这对我来说是相当新的东西,但是它帮助我调试了视图的框架(以及文本和类型)。我的一个子视图没有显示在屏幕上,因此使用了recursiveDescription,我意识到我的一个0子视图的宽度是...,所以我去纠正了它的约束,出现了子视图。


0

或者,如果您想从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

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

或者,如果要查找特定类型的所有子视图,请使用:

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

我已经完成了一个类别,UIView只需调用传递索引的函数即可以漂亮的树格式打印它们。这只是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++];
        }
    }
}

希望对您有所帮助:)


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维护视图的层次结构。要获取uitableviewcell的子视图,您必须执行以下操作。

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.