iOneWay Blog

天道地道,自求我道

QQ:373850874. 欢迎加入。


如何检测任意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 {

    // 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类的子视图是私有的,因此他们将会在将来更改这些类。所以以上方法只能在你没有其他方式的情况下谨慎的使用。

如有错误请指正,谢谢

最近的文章

iOS 7 SDK: 多任务的改进

原文地址:http://code.tutsplus.com/tutorials/ios-7-sdk-multitasking-enhancements--mobile-20295 这篇教程向我们介绍i…

继续阅读
更早的文章

如何添加 Quick actions

原文地址:http://iostuts.io/2015/10/08/how-to-add-quick-actions/ 这篇教程介绍了Quick actions是如何工作的,如何创建Quick act…

继续阅读