檢測任意view的subviews的關系樹

原文地址: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)
        }
    }

}

如有錯誤請指正,謝謝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容