iOS开发iOS开发iOS开发交流

检测任意view的subviews的关系树

2016-05-20  本文已影响672人  iOneWay

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

}

如有错误请指正,谢谢

上一篇下一篇

猜你喜欢

热点阅读