Is there a way I can get ALL the views and subviews and subviews of these subviews (you get the idea...) of an NSWindow?
Thanks.
Is there a way I can get ALL the views and subviews and subviews of these subviews (you get the idea...) of an NSWindow?
Thanks.
Here is a category on NSView:
@interface NSView (MDRecursiveSubviews)
- (NSArray *)md__allSubviews;
@end
@implementation NSView (MDRecursiveSubviews)
- (NSArray *)md__allSubviews {
NSMutableArray *allSubviews = [NSMutableArray arrayWithObject:self];
NSArray *subviews = [self subviews];
for (NSView *view in subviews) {
[allSubviews addObjectsFromArray:[view md__allSubviews]];
}
return [[allSubviews copy] autorelease];
}
@end
With a quick nib file I created with a view hierarchy, it printed this:
[RecursiveSubviewsAppDelegate awakeFromNib] allSubviews == (
"<NSView: 0x10390dfd0>",
"<NSView: 0x103c07ae0>",
"<NSView: 0x100129cc0>",
"<NSButton: 0x100115ce0>",
"<NSButton: 0x100116900>",
"<NSButton: 0x1001165c0>",
"<NSButton: 0x100116130>",
"<NSButton: 0x100114ad0>",
"<NSButton: 0x100115910>",
"<NSButton: 0x100115090>",
"<NSScrollView: 0x103b07a30>",
"<NSClipView: 0x103b07d40>",
"<NSTextView: 0x103b083c0>\n
Frame = {{0.00, 0.00}, {159.00, 58.00}},
Bounds = {{0.00, 0.00}, {159.00, 58.00}}\n
Horizontally resizable: NO, Vertically resizable: YES\n
MinSize = {159.00, 58.00}, MaxSize = {463.00, 10000000.00}\n",
"<NSScroller: 0x1001145b0>",
"<NSScroller: 0x100114840>",
"<NSScrollView: 0x10390ea00>",
"<NSClipView: 0x10390ef10>",
"<NSTableView: 0x10390f570>",
"<NSScroller: 0x103b06f10>",
"<NSScroller: 0x103b07460>",
"<NSClipView: 0x1039105d0>",
"<NSTableHeaderView: 0x103910300>",
"<_NSCornerView: 0x103911c20>"
One note of concern I should add is that it's unclear to me how this would be useful, except as a debugging tool. But even then, there are probably easier ways of doing things.
static void dumpViews(NSView* v, int level) {
NSString* indent = @"";
for (int i = 0; i < level; i++) {
indent = [indent stringByAppendingString:@" "];
}
NSLog(@"%@%@ %@", indent, [v class], NSStringFromRect(v.frame));
if (v.subviews != null) {
for (id s in v.subviews) {
dumpViews(s, level + 1);
}
}
}
- (void) windowControllerDidLoadNib: (NSWindowController*) controller {
NSWindow* window = controller.window;
dumpViews(window.contentView, 0);
...
If I understood you correctly, you would have to create a method that calls itself recursively. Something like this:
- (NSArray *)allSubviewsOfView:(NSView *)view
{
NSMutableArray *subviews = [[view subviews] mutableCopy];
for (NSView *subview in [view subviews])
[subviews addObjectsFromArray:[self allSubviewsOfView:subview]]; //recursive
return subviews;
}
You would then call something like
NSArray *allSubviewsOfWindow = [self allSubviewsOfView:[window contentView]];
to get your views. (And don't forget to do memory management if you're not using GC.)
Not sure about infinite recursion in the other answers but you definitely can't modify an array while you are doing a fast enumeration on it. Here's a method I wrote for iOS that iterates through all subviews until there aren't any more left to explore. I assume the same or similar would work for NSView. Hope this helps.
(Edit: now… if I had just looked a little further down my search results, I would have found this really easy way to do it first: How can I loop through all subviews of a UIView, and their subviews and their subviews )
- (NSMutableArray *)allSubviewsInView:(UIView *)parentView {
NSMutableArray *allSubviews = [[NSMutableArray alloc] initWithObjects: nil];
NSMutableArray *currentSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
NSMutableArray *newSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
while (newSubviews.count) {
[newSubviews removeAllObjects];
for (UIView *view in currentSubviews) {
for (UIView *subview in view.subviews) [newSubviews addObject:subview];
}
[currentSubviews removeAllObjects];
[currentSubviews addObjectsFromArray:newSubviews];
[allSubviews addObjectsFromArray:newSubviews];
}
NSLog(@"\n%d total subviews:\n%@",allSubviews.count, allSubviews);
return allSubviews;
}
Logged results from a sample view:
18 total subviews:
(
"<UIRoundedRectButton: 0x6a7a590; frame = (26 20; 72 37); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x6a772d0>>",
"<UISwitch: 0x6a7f930; frame = (26 76; 79 27); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x6a7fa20>>",
"<UIImageView: 0x685e4a0; frame = (82 20; 139 139); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x685eca0>>",
"<UITextView: 0x6893e40; frame = (20 196; 192 79); text = 'Lorem ipsum dolor sit er ...'; clipsToBounds = YES; autoresize = RM+BM; layer = <CALayer: 0x687c330>; contentOffset: {0, 0}>",
"<UIView: 0x6a88af0; frame = (26 304; 198 123); autoresize = RM+BM; tag = 1; layer = <CALayer: 0x6a88b20>>",
"<UIButtonLabel: 0x6a7f410; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6a7f4d0>>",
"<_UISwitchInternalView: 0x6a7fa50; frame = (-1 0; 79 27); layer = <CALayer: 0x6a79b90>>",
"<UITextSelectionView: 0x6894070; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x68940d0>>",
"<UIImageView: 0x68924b0; frame = (0 72; 192 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x6892520>>",
"<UIImageView: 0x6894100; frame = (185 0; 7 79); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x6894180>>",
"<UIWebDocumentView: 0x7416600; frame = (0 0; 192 394); text = 'Lorem ipsum dolor sit er ...'; opaque = NO; userInteractionEnabled = NO; layer = <UIWebLayer: 0x6895330>>",
"<UIView: 0x6a88b50; frame = (10 10; 80 80); autoresize = W+H; tag = 2; layer = <CALayer: 0x6a88b80>>",
"<UIImageView: 0x6a80610; frame = (1 0; 77 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6a80650>>",
"<UIView: 0x6a806f0; frame = (1 0; 77 27); clipsToBounds = YES; alpha = 0; layer = <CALayer: 0x6a80720>>",
"<UIView: 0x6a88fa0; frame = (5 5; 50 50); autoresize = W+H; tag = 3; layer = <CALayer: 0x6a88fd0>>",
"<UIImageView: 0x6a808e0; frame = (-2 0; 79 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6a84d30>>",
"<UIImageView: 0x6a84c00; frame = (-2 0; 131 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6a84d90>>",
"<UIImageView: 0x6a84c40; frame = (49 0; 29 27); userInteractionEnabled = NO; layer = <CALayer: 0x6a84c80>>"
)
The above solution will not work if there are views within views. We would need to do a recursive solution as shown below.
-(void) printViewHierarchy: (UIView * ) view withTag: (NSInteger) tag {
if (view == nil) {
return;
}
if (view == nil || [view tag] == tag) {
NSLog(@"%@", view);
return;
}
for (UIView * subview in [view subviews]) {
[self printViewHierarchy: subview withTag: tag];
}
}
- (void) findAllViews {
UIView * baseView = [UIView new];
[baseView setTag: 1];
UIView * secondRowA = [UIView new];
[secondRowA setTag: 2];
UIView * secondRowB = [UIView new];
[secondRowB setTag: 3];
UIView * thirdRowA = [UIView new];
[thirdRowA setTag: 4];
UIView * thirdRowB = [UIView new];
[thirdRowB setTag: 5];
[secondRowA addSubview: thirdRowA];
[secondRowB addSubview: thirdRowB];
[baseView addSubview: secondRowA];
[baseView addSubview: secondRowB];
[self printViewHierarchy: baseView withTag: 6];
}