How can I loop through all subviews of a UIView, and their subviews and their subviews

IphoneObjective CLoopsUiviewSubview

Iphone Problem Overview


How can I loop through all subviews of a UIView, and their subviews and their subviews?

Iphone Solutions


Solution 1 - Iphone

Use 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];

Solution 2 - Iphone

Well here is my solution using recursion and a wrapper(category/extension) for the UIView class.

// 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

Usage : Now you should be looping through all the sub views and manipulate them as needed.

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

Solution 3 - Iphone

Here's another Swift implementation:

extension UIView {
    var allSubviews: [UIView] {
        return self.subviews.flatMap { [$0] + $0.allSubviews }
    }
}

Solution 4 - Iphone

A solution in Swift 3 that gives all subviews without including the view itself:

extension UIView {
var allSubViews : [UIView] {
    
        var array = [self.subviews].flatMap {$0}
    
        array.forEach { array.append(contentsOf: $0.allSubViews) }
    
        return array
    }
}

Solution 5 - Iphone

Just found an interesting way to do this through the debugger:

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

references this Apple Technote:

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

Just make sure your debugger is paused (either set a break point of pause it manually) and you can ask for the recursiveDescription.

Solution 6 - Iphone

I tag everything when it's created. Then it's easy to find any subview.

view = [aView viewWithTag:tag];

Solution 7 - Iphone

Here is an example with actual view looping and breaking functionality.

Swift:

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) }
		}
	}

}

Call example:

mainView.loopViewHierarchy { (view, stop) in
	if view is UIButton {
		/// use the view
		stop = true
	}
}

Reversed looping:

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
	}
}

Call example:

mainView.loopViewHierarchyReversed { (view, stop) in
	if view is UIButton {
		/// use the view
		stop = true
	}
}

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

Call example:

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

Solution 8 - Iphone

With the help of Ole Begemann. I added a few lines to incorporate the block concept into it.

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

Using the HierarchyLogging category in your ViewController. You are now have freedom to what you need to do.

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

Solution 9 - Iphone

Here is a recursive code:-

 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];
  }
}

Solution 10 - Iphone

Need not create any new function. Just do it when debugging with Xcode.

Set a breakpoint in a view controller, and make the app pause at this breakpoint.

Right click the empty area and press "Add Expression..." in Xcode's Watch window.

Input this line:

(NSString*)[self->_view recursiveDescription]

If the value is too long, right click it and choose "Print Description of ...". You will see all subviews of self.view in the console window. Change self->_view to something else if you don't want to see subviews of self.view.

Done! No gdb!

Solution 11 - Iphone

By the way, I made an open source project to help with this sort of task. It's really easy, and uses Objective-C 2.0 blocks to execute code on all views in a hierarchy.

https://github.com/egold/UIViewRecursion

Example:

-(void)makeAllSubviewsGreen
{
    [self.view runBlockOnAllSubviews:^(UIView *view) {

        view.backgroundColor = [UIColor greenColor];
    }];
}

Solution 12 - Iphone

Here is a variation on Ole Begemann's answer above, which adds indentation to illustrate the hierarchy:

// 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

Solution 13 - Iphone

The code posted in this answer traverses all windows and all views and all of their subviews. It was used to dump a printout of the view hierarchy to NSLog but you can use it as a basis for any traversal of the view hierarchy. It uses a recursive C function to traverse the view tree.

Solution 14 - Iphone

I wrote a category some time back to debug some views.

IIRC, the posted code is the one that worked. If not, it will point you in the right direction. Use at own risk, etc.

Solution 15 - Iphone

This displays the hierarchy level as well

@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(int)level
{
    NSLog(@"%d - %@", level, self);
    for (UIView *subview in self.subviews)
    {
        [subview logViewHierarchy:(level+1)];
    }
}
@end

Solution 16 - Iphone

Wish I'd found this page first, but if (for some reason) you want to do this non-recursively, not in a Category, and with more lines of code

Solution 17 - Iphone

I think all of the answers using recursion (except for the debugger option) used categories. If you don't need/want a category, you can just use a instance method. For instance, if you need to get an array of all labels in your view hierarchy, you could do this.

@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
    }
}

Solution 18 - Iphone

The method below creates one or more mutable arrays, then loops through the subviews of the input view. In doing so it adds the initial subview then queries as to whether there are any subviews of that subview. If true, it calls itself again. It does so until the all the views of the hierarchy have been added.

-(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;
}

Call using:

NSArray * subviews = [self allSubs:someView];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question123hal321View Question on Stackoverflow
Solution 1 - IphoneOle BegemannView Answer on Stackoverflow
Solution 2 - IphoneRamaKrishna ChunduriView Answer on Stackoverflow
Solution 3 - IphoneCal StephensView Answer on Stackoverflow
Solution 4 - IphoneielyamaniView Answer on Stackoverflow
Solution 5 - Iphonedjibouti33View Answer on Stackoverflow
Solution 6 - IphoneCorey DonCorsean RickettsView Answer on Stackoverflow
Solution 7 - IphoneVahram DodoryanView Answer on Stackoverflow
Solution 8 - IphonedocchangView Answer on Stackoverflow
Solution 9 - IphoneLeenaView Answer on Stackoverflow
Solution 10 - IphoneVince YuanView Answer on Stackoverflow
Solution 11 - IphoneEric GView Answer on Stackoverflow
Solution 12 - IphonejaredsinclairView Answer on Stackoverflow
Solution 13 - IphoneprogrmrView Answer on Stackoverflow
Solution 14 - IphoneTechZenView Answer on Stackoverflow
Solution 15 - IphonesnezView Answer on Stackoverflow
Solution 16 - IphonesmlxlView Answer on Stackoverflow
Solution 17 - IphoneDonovanView Answer on Stackoverflow
Solution 18 - IphoneJohnny RockexView Answer on Stackoverflow