我如何循环浏览UIView的所有子视图,以及它们的子视图和子视图
我如何循环访问UIView的所有子视图,以及它们的子视图和子视图?
使用recursion:
// UIView+HierarchyLogging.h @interface UIView (ViewHierarchyLogging) - (void)logViewHierarchy; @end // UIView+HierarchyLogging.m @implementation UIView (ViewHierarchyLogging) - (void)logViewHierarchy { NSLog(@"%@", self); for (UIView *subview in self.subviews) { [subview logViewHierarchy]; } } @end // In your implementation [myView logViewHierarchy];
那么这里是我的解决scheme使用recursion和UIView类的包装(类别/扩展)。
// UIView+viewRecursion.h @interface UIView (viewRecursion) - (NSMutableArray*) allSubViews; @end // UIView+viewRecursion.m @implementation UIView (viewRecursion) - (NSMutableArray*)allSubViews { NSMutableArray *arr=[[[NSMutableArray alloc] init] autorelease]; [arr addObject:self]; for (UIView *subview in self.subviews) { [arr addObjectsFromArray:(NSArray*)[subview allSubViews]]; } return arr; } @end
用法:现在你应该循环所有的子视图,并根据需要操作它们。
//disable all text fields for(UIView *v in [self.view allSubViews]) { if([v isKindOfClass:[UITextField class]]) { ((UITextField*)v).enabled=NO; } }
创build时标记所有内容。 那么很容易find任何子视图。
view = [aView viewWithTag:tag];
刚刚find一个有趣的方式来通过debugging器来做到这一点:
http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/
引用这个Apple Technote:
http://developer.apple.com/library/ios/#technotes/tn2010/tn2239.html#SECUIKIT
只要确保你的debugging器已经暂停(或者设置一个暂停点来手工暂停),你可以要求recursiveDescription
。
Swift 3中的一个解决scheme,它提供了所有的subviews
而不包括视图本身:
extension UIView { var allSubViews : [UIView] { var array = [self.subviews].flatMap {$0} array.forEach { array.append(contentsOf: $0.allSubViews) } return array } }
在Ole Begemann的帮助下。 我添加了几行将块概念合并到其中。
UIView的+ HierarchyLogging.h
typedef void (^ViewActionBlock_t)(UIView *); @interface UIView (UIView_HierarchyLogging) - (void)logViewHierarchy: (ViewActionBlock_t)viewAction; @end
UIView的+ HierarchyLogging.m
@implementation UIView (UIView_HierarchyLogging) - (void)logViewHierarchy: (ViewActionBlock_t)viewAction { //view action block - freedom to the caller viewAction(self); for (UIView *subview in self.subviews) { [subview logViewHierarchy:viewAction]; } } @end
在ViewController中使用HierarchyLogging类别。 你现在有自由,你需要做什么。
void (^ViewActionBlock)(UIView *) = ^(UIView *view) { if ([view isKindOfClass:[UIButton class]]) { NSLog(@"%@", view); } }; [self.view logViewHierarchy: ViewActionBlock];
顺便说一句,我做了一个开源项目来帮助完成这样的任务。 这非常简单,并且使用Objective-C 2.0块在层次结构中的所有视图上执行代码。
https://github.com/egold/UIViewRecursion
例:
-(void)makeAllSubviewsGreen { [self.view runBlockOnAllSubviews:^(UIView *view) { view.backgroundColor = [UIColor greenColor]; }]; }
不需要创build任何新的function。 只要用Xcode进行debugging就可以了。
在视图控制器中设置断点,并使应用程序在此断点处暂停。
右键单击空白区域,然后在Xcode的“监视”窗口中按“添加expression式…”。
input这一行:
(NSString*)[self->_view recursiveDescription]
如果值太长,请右键单击它并select“打印…的说明”。 您将在控制台窗口中看到self.view的所有子视图。 如果您不想查看self.view的子视图,请将self – > _ view更改为其他内容。
完成! 没有gdb!
这是一个recursion的代码:
for (UIView *subViews in yourView.subviews) { [self removSubviews:subViews]; } -(void)removSubviews:(UIView *)subView { if (subView.subviews.count>0) { for (UIView *subViews in subView.subviews) { [self removSubviews:subViews]; } } else { NSLog(@"%i",subView.subviews.count); [subView removeFromSuperview]; } }
下面是Ole Begemann上面的答案的一个变种,它增加了缩进以说明层次:
// UIView+HierarchyLogging.h @interface UIView (ViewHierarchyLogging) - (void)logViewHierarchy:(NSString *)whiteSpaces; @end // UIView+HierarchyLogging.m @implementation UIView (ViewHierarchyLogging) - (void)logViewHierarchy:(NSString *)whiteSpaces { if (whiteSpaces == nil) { whiteSpaces = [NSString string]; } NSLog(@"%@%@", whiteSpaces, self); NSString *adjustedWhiteSpaces = [whiteSpaces stringByAppendingFormat:@" "]; for (UIView *subview in self.subviews) { [subview logViewHierarchy:adjustedWhiteSpaces]; } } @end
这是一个具有实际视图循环和打破function的例子。
typedef void(^ViewBlock)(UIView* view, BOOL* stop); @interface UIView (ViewExtensions) -(void) loopViewHierarchy:(ViewBlock) block; @end @implementation UIView (ViewExtensions) -(void) loopViewHierarchy:(ViewBlock) block { BOOL stop = NO; if (block) { block(self, &stop); } if (!stop) { for (UIView* subview in self.subviews) { [subview loopViewHierarchy:block]; } } } @end
通话示例:
[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) { if ([view isKindOfClass:[UIButton class]]) { /// use the view *stop = YES; } }];
此答案中发布的代码遍历所有窗口和所有视图及其所有子视图。 它被用来转储视图层次结构打印输出到NSLog,但您可以使用它作为任何遍历视图层次结构的基础。 它使用recursionC函数遍历视图树。
我后来写了一个类来debugging一些视图。
IIRC,发布的代码是工作的。 如果不是的话,它会指向你正确的方向。 自担风险等。
这也显示层级
@implementation UIView (ViewHierarchyLogging) - (void)logViewHierarchy:(int)level { NSLog(@"%d - %@", level, self); for (UIView *subview in self.subviews) { [subview logViewHierarchy:(level+1)]; } } @end
希望我已经find了第一个页面 ,但是如果(出于某种原因)你想以非recursion的方式来做,而不是在一个Category中,
我认为使用recursion的所有答案(除了debugging器选项)使用的类别。 如果你不需要/需要一个类别,你可以使用一个实例方法。 例如,如果您需要获取视图层次结构中所有标签的数组,您可以这样做。
@interface MyViewController () @property (nonatomic, retain) NSMutableArray* labelsArray; @end @implementation MyViewController - (void)recursiveFindLabelsInView:(UIView*)inView { for (UIView *view in inView.subviews) { if([view isKindOfClass:[UILabel class]]) [self.labelsArray addObject: view]; else [self recursiveFindLabelsInView:view]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.labelsArray = [[NSMutableArray alloc] init]; [self recursiveFindLabelsInView:self.view]; for (UILabel *lbl in self.labelsArray) { //Do something with labels } }
下面的方法创build一个或多个可变数组,然后遍历input视图的子视图。 这样做会添加最初的子视图,然后查询是否有子视图的任何子视图。 如果这是真的,它会再次调用自己。 这样做直到层次结构的所有视图都被添加为止。
-(NSArray *)allSubs:(UIView *)view { NSMutableArray * ma = [NSMutableArray new]; for (UIView * sub in view.subviews){ [ma addObject:sub]; if (sub.subviews){ [ma addObjectsFromArray:[self allSubs:sub]]; } } return ma; }
使用:
NSArray * subviews = [self allSubs:someView];