原文地址:http://www.glimsoft.com/01/07/how-to-inspect-subviews-hierarchy-of-any-uiview/
一个简单的方式打印出任何UIView整个子视图层次(子类)。我们要通过创建UIView类,并添加一个递归方法来查看整个树结构。
现在,可能有人想知道什么是‘category’:它是一种不需要继承任何类来向类中添加方法的一种简洁方式。想了解更多可以参考官方文档:documentation
在某些情况下,当你在寻找一些基础的东西,比如MKMapView的组成;为了修改某些标签,等等,我觉得这种情况很多,所以没有必要再举例了。
以下是我们的一个category(file UIView+printSubviews.h):
#import <Foundation/Foundation.h>
@interface UIView (PrintSubviews)
- (void)printSubviewsWithIndentation:(int)indentation;
@end
Objective-C的Category声明十分简单且重要。更重要的是我们的方法可以在任何UIView及其子类(UIScrollView, MKMapView, UITableView, UIButton 等等)中工作。现在看看category的实现文件,如下代码:
#import "UIView+printSubviews.h"
@implementation UIView (PrintSubviews)
- (void)printSubviewsWithIndentation:(int)indentation {
NSArray *subviews = [self subviews];
for (int i = 0; i < [subviews count]; i++) {
UIView *currentSubview = [subviews objectAtIndex:i];
NSMutableString *currentViewDescription = [[NSMutableString alloc] init];
for (int j = 0; j <= indentation; j++) {
[currentViewDescription appendString:@" "];
}
[currentViewDescription appendFormat:@"[%d]: class: '%@', frame=(%.1f, %.1f, %.1f, %.1f), opaque=%i, hidden=%i, userInterfaction=%i", i, NSStringFromClass([currentSubview class]), currentSubview.frame.origin.x, currentSubview.frame.origin.y, currentSubview.frame.size.width, currentSubview.frame.size.height, currentSubview.opaque, currentSubview.hidden, currentSubview.userInteractionEnabled];
fprintf(stderr,"%s\n", [currentViewDescription UTF8String]);
[currentSubview printSubviewsWithIndentation:indentation+1];
}
}
@end
我觉得不需要作太多的解释,因为十分的简单。现在我将给大家展示一个使用实例,以及该方法所产生的输出。我要在一个有多个annotations,其中一个annotation被选中并弹出callout view的MKMapView上使用改方法。
首先你需要在你将要使用的类中引入这个category的头文件:
#import "UIView+printSubviews.h"
然后在你希望检测的view上调用-printSubviewsWithIndentation:
方法
NSLog(@"*** Printing out all the subviews of MKMapView ***");
[mapView printSubviewsWithIndentation:0];
现在你可以看到如下的输出:
现在你可以看到mapView中所有藏在屏幕后面的view了,每一行输出前面的[]中的数字代表该行的view在树结构中的层次索引。
你可以向下面这样获取到MKAnnotationContainerView
UIView *annotationContainerView = [[[[[[mapView subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:1];
当然,这种获取方式是不安全的,因为它取决于视图的特定顺序。所以你在使用以上方法获取对应视图的时候必须对它做一次测试:
if [subviews count] > 0,或者使用像下面这样使用 NSClassFromString():
for (UIView *subview in subviews) {
if ([subview isKindOfClass:NSClassFromString(@"NameOfTheClass")]) {
// We found the subview that we want
}
}
最后我想提示大家,苹果不希望开发者这样做,因为一些UIKit类的子视图是私有的,因此他们将会在将来更改这些类。所以以上方法只能在你没有其他方式的情况下谨慎的使用。
Swift版本
extension UIView {
func printSubviewsWithIndentation(indentation: Int) -> Void {
for i in 0 ..< subviews.count {
let currentView = subviews[i]
var description = String()
for _ in 0...indentation {
description += " "
}
description += "[\(i)]: class:\(NSStringFromClass(currentView.dynamicType)): frame=\(currentView.frame), opaque=\((Int)(currentView.opaque)),hidden=\((Int)(currentView.hidden.boolValue)), userInteraction=\((Int)(currentView.userInteractionEnabled))"
print(description)
currentView.printSubviewsWithIndentation(indentation + 1)
}
}
}
如有错误请指正,谢谢