原文地址: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)
}
}
}
如有錯誤請指正,謝謝