如何遍历UIView的所有子视图及其子视图和子视图

123*_*321 76 iphone loops objective-c subview uiview

如何遍历UIView的所有子视图及其子视图和子视图?

Ole*_*ann 120

使用递归:

// 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];
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法在logViewHierarchy中传递一个函数?这样它可以在不同的时间满足您的需求. (2认同)
  • @docchang:Obj-C块非常适合该用例.将块传递给方法,执行块并在每次方法调用时将其传递给层次结构. (2认同)

Ram*_*uri 33

那么这里是我的解决方案,使用递归和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
Run Code Online (Sandbox Code Playgroud)

用法:现在您应该循环遍历所有子视图并根据需要操作它们.

//disable all text fields
for(UIView *v in [self.view allSubViews])
{
     if([v isKindOfClass:[UITextField class]])
     {
         ((UITextField*)v).enabled=NO;
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 注意`allSubViews`函数中存在内存泄漏:你必须创建数组作为`[[[NSMutableArray alloc] init] autorelease]`或者作为`[NSMutableArray array]`(它是相同的). (3认同)

iel*_*ani 15

Swift 3中的解决方案,它提供了所有subviews不包含视图本身的解决方案:

extension UIView {
var allSubViews : [UIView] {

        var array = [self.subviews].flatMap {$0}

        array.forEach { array.append(contentsOf: $0.allSubViews) }

        return array
    }
}
Run Code Online (Sandbox Code Playgroud)


dji*_*i33 12

刚刚通过调试器找到了一种有趣的方法:

http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/

引用此Apple技术说明:

https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT

只需确保您的调试器已暂停(或者手动设置暂停的中断点),您可以要求recursiveDescription.


小智 11

我在创建时标记所有内容.然后很容易找到任何子视图.

view = [aView viewWithTag:tag];
Run Code Online (Sandbox Code Playgroud)


Cal*_*ens 8

这是另一个Swift实现:

extension UIView {
    var allSubviews: [UIView] {
        return self.subviews.flatMap { [$0] + $0.allSubviews }
    }
}
Run Code Online (Sandbox Code Playgroud)


doc*_*ang 7

在Ole Begemann的帮助下.我添加了几行来将块概念合并到其中.

UIView的+ HierarchyLogging.h

typedef void (^ViewActionBlock_t)(UIView *);
@interface UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction;
@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

在ViewController中使用HierarchyLogging类别.您现在可以自由地完成您需要做的事情.

void (^ViewActionBlock)(UIView *) = ^(UIView *view) {
    if ([view isKindOfClass:[UIButton class]]) {
        NSLog(@"%@", view);
    }
};
[self.view logViewHierarchy: ViewActionBlock];
Run Code Online (Sandbox Code Playgroud)


Vah*_*yan 5

以下是实际视图循环和中断功能的示例.

迅速:

extension UIView {

    func loopViewHierarchy(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        var stop = false
        block(self, &stop)
        if !stop {
            self.subviews.forEach { $0.loopViewHierarchy(block: block) }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

电话示例:

mainView.loopViewHierarchy { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}
Run Code Online (Sandbox Code Playgroud)

反向循环:

extension UIView {

    func loopViewHierarchyReversed(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        for i in stride(from: self.highestViewLevel(view: self), through: 1, by: -1) {
            let stop = self.loopView(view: self, level: i, block: block)
            if stop {
                break
            }
        }
    }

    private func loopView(view: UIView, level: Int, block: (_ view: UIView, _ stop: inout Bool) -> ()) -> Bool {
        if level == 1 {
            var stop = false
            block(view, &stop)
            return stop
        } else if level > 1 {
            for subview in view.subviews.reversed() {
            let stop = self.loopView(view: subview, level: level - 1, block: block)
                if stop {
                    return stop
                }
            }
        }
        return false
    }

    private func highestViewLevel(view: UIView) -> Int {
        var highestLevelForView = 0
        for subview in view.subviews.reversed() {
            let highestLevelForSubview = self.highestViewLevel(view: subview)
            highestLevelForView = max(highestLevelForView, highestLevelForSubview)
        }
        return highestLevelForView + 1
    }
}
Run Code Online (Sandbox Code Playgroud)

电话示例:

mainView.loopViewHierarchyReversed { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}
Run Code Online (Sandbox Code Playgroud)

Objective-C的:

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
Run Code Online (Sandbox Code Playgroud)

电话示例:

[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) {
    if ([view isKindOfClass:[UIButton class]]) {
        /// use the view
        *stop = YES;
    }
}];
Run Code Online (Sandbox Code Playgroud)