获取NSWindow的所有视图和子视图

12

有没有一种方法可以获取NSWindow的所有视图、子视图以及子视图的子视图(你懂的……)?

谢谢。


3
欢迎来到 StackOverflow!为什么不将您的用户名从 user635064 更改为更独特的名称呢?此外,如果您标记正确答案,您将更有可能得到帮助。 - Moshe
2
同意关于用户名的问题,但不知道你为什么告诉我如何标记问题正确?如果它对我有帮助,我总是会标记一个问题作为正确的... - user635064
只需将您的视图添加到NSMutableArray中以便稍后枚举。 - Sparky
7个回答

10

这里是关于 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

使用我创建的带有视图层次结构的快速nib文件,输出了以下内容:

[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>"

我应该补充一点值得关注的事情,就是除了作为一个调试工具之外,我不清楚这会有什么用处。但即使是作为调试工具,可能也有更简单的做法。


也适用于iOS:NS -> UI。 - clozach
不支持ARC:***-[NSMutableArray addObjectsFromArray:]:数组参数不是NSArray。 - Sparky
“更简单的做法”是将您的视图添加到NSMutableArray中,并使该NSMutableArray在更大的范围内可访问。这样做也更有效率。 - Sparky

9

可以向NSView发送私人信息以打印控件层次结构。

[NSView _subtreeDescription]会给出NSView的整个层次结构,即它的子级及其子级。


4
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);
    ...

3
如果我理解正确,您需要创建一个递归调用自身的方法。类似这样的代码:
- (NSArray *)allSubviewsOfView:(NSView *)view
{
  NSMutableArray *subviews = [[view subviews] mutableCopy];
  for (NSView *subview in [view subviews])
    [subviews addObjectsFromArray:[self allSubviewsOfView:subview]]; //recursive
  return subviews;
}

您可以调用类似以下的代码:
NSArray *allSubviewsOfWindow = [self allSubviewsOfView:[window contentView]];

获取您的视图。(如果您不使用GC,请不要忘记进行内存管理。)


1
@Enchilada:不要忘记释放数组。@user635064:不,它不会无限递归。如果您将视图添加为其自己子视图的子视图,则可能会发生这种情况,但那时它将是一个闭合循环,无法从窗口的“contentView”访问。 - Peter Hosey
2
@Enchilada:这种方法的真正问题不在于它无限递归,而在于你正在枚举一个你正在改变的数组。 - Peter Hosey
为什么这个方法不会无限递归?只要没有显式的 return 语句(或类似的控制语句),该方法就会无限递归。 - Moshe
1
啊,无法编辑我的先前评论。这是一个更好的:@Moshe:它只会递归到视图层次结构中存在的视图数量。因此,除非您可以创建具有无限数量子视图的nib文件或以编程方式创建视图层次结构(其行为本身将导致“第一个”无限递归而不是在调用该方法时遇到的任何递归),否则不会出现无限递归。 - NSGod
我建议将本地变量'subviews'重命名为其他名称,例如collectedViews,以避免混淆。 - Rob van der Veer
显示剩余6条评论

2
不确定其他答案中是否存在无限递归的情况,但是在使用快速枚举时不能修改数组。这里是我为iOS编写的一个方法,它遍历所有子视图,直到没有任何可探索的视图为止。我假设NSView也可以使用类似的方法。希望对您有所帮助。
(编辑:现在......如果我只是再往下看一点我的搜索结果,我将会发现这个非常简单的方法首先要做的是:如何循环遍历UIView的所有子视图及其子视图和子视图
- (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;

}

从示例视图记录的结果:

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

0

如果视图中包含其他视图,则上述解决方案将无法正常工作。我们需要使用下面所示的递归解决方案。

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


-2
NSArray *arrofView = [[self view] subviews];

OP正在寻找子视图及其子视图(等等,我想)。 - Monolo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接