如何在iOS中列出一个UIViewController中的所有子视图?

94

我想列出UIViewController中的所有子视图。我尝试了self.view.subviews,但并没有列出所有子视图,例如UITableViewCell中的子视图未被找到。有什么建议吗?


您可以通过递归搜索找到所有的子视图。例如,检查子视图是否有子视图。 - Mahesh
22个回答

176

你需要递归迭代子视图。

- (void)listSubviewsOfView:(UIView *)view {
    
    // Get the subviews of the view
    NSArray *subviews = [view subviews];

    for (UIView *subview in subviews) {
        
        // Do what you want to do with the subview
        NSLog(@"%@", subview);

        // List the subviews of subview
        [self listSubviewsOfView:subview];
    }
}

20
从技术角度来看,您不需要检查子视图计数是否为0。 - Greg Maletic
5
这行代码输出与您的结果相同! 翻译内容:该行代码将打印与你的结果相同的内容。 - Hemang
如果你在这里,请务必查看@natbro的“recursiveDescription”答案,它要简单得多- https://dev59.com/amw05IYBdhLWcg3wcRUj#8962824 - Felipe Sabino
1
这是我对@EmptyStack解决方案的改进。它递归缩进显示类名和框架坐标。 - Pierre de LESPINAY

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] - Pawan Sharma
1
@WildPointer他在谈论控制台。你需要在某个地方设置断点来执行此操作。PO =打印对象。 - SmileBot
1
+1 苹果推荐这种方法(来自WWDC 2013年Cocoa和Cocoa Touch会议的隐藏宝石演讲,第5个小贴士)。 - Slipp D. Thompson
@पवन请看我在这里的回答(https://dev59.com/amw05IYBdhLWcg3wcRUj#46936317)。我对这个答案进行了重写。 - mfaani

21

优雅的递归解决方案 (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

这是Swift版本

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

8

细节

  • 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)")

结果

这里输入图片描述


这是一个展示IT技术相关内容的图片。

这非常有帮助,能够确定一个麻烦的子视图 - 谢谢。 - Nostradamus

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
如果您只想要一个UIView数组,这是一个一行解决方案(Swift 4+):
extension UIView {
  var allSubviews: [UIView] {
    return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews }
  }
}

谢谢这个解决方案!我花了好几个小时来琢磨,但是你的帖子让我省下了不少时间! - user2525211

5
我使用这种方法:
NSLog(@"%@", [self.view subviews]);

在UIViewController中。


4

在我看来,UIView的类别或扩展比其他方法更好,递归是获取所有子视图的关键点。

了解更多:

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

直接使用sequence函数获取所有子视图。

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 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接