原文地址: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 {
// Get all the subviews of the current view
NSArray *subviews = [self subviews];
// Loop through the whole subviews array. We are using the plain-old C-like for loop,
// just for its simplicity and also to be provided with the iteration number
for (int i = 0; i < [subviews count]; i++) {
// Get the subview at current index
UIView *currentSubview = [subviews objectAtIndex:i];
// We will create our description using this mutable string
NSMutableString *currentViewDescription = [[NSMutableString alloc] init];
// Indent the actual description to provide visual clue of how deeply is the current view nested
for (int j = 0; j <= indentation; j++) {
[currentViewDescription appendString:@" "];
}
// Construct the actual description string. Note that we are using just index of the current view
// and name of its class, but it's up to you to print anything you are interested in
// (for example the frame property using the NSStringFromCGRect(currentSubview.frame) )
[currentViewDescription appendFormat:@"[%d]: class: '%@'", i, NSStringFromClass([currentSubview class])];
// Log the description string to the console
NSLog(@"%@", currentViewDescription);
// Be good memory citizen
[currentViewDescription release];
// the 'recursiveness' nature of this method. Call it on the current subview, with greater indentation
[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类的子视图是私有的,因此他们将会在将来更改这些类。所以以上方法只能在你没有其他方式的情况下谨慎的使用。
如有错误请指正,谢谢
