Получить ВСЕ представления и подвид NSWindow

Есть ли способ получить ВСЕ представления, подпредставления и подпредставления этих подпредставлений (вы поняли ...) NSWindow?

Спасибо.


person user635064    schedule 27.03.2011    source источник
comment
Добро пожаловать в StackOverflow! Почему бы не изменить свое имя пользователя с user635064 на более уникальное? Кроме того, вы с большей вероятностью получите помощь, если отметите правильные ответы как таковые.   -  person Moshe    schedule 27.03.2011
comment
Согласны с именем пользователя, но не знаете, почему вы говорите мне о правильной маркировке вопроса? Я всегда отмечаю вопрос правильным, если он мне помог ...   -  person user635064    schedule 27.03.2011
comment
Просто добавьте свои представления в NSMutableArray для последующего перечисления.   -  person Sparky    schedule 26.04.2018


Ответы (6)


Вот категория в NSView:

@interface NSView (MDRecursiveSubviews)
- (NSArray *)allSubviews;
@end

@implementation NSView (MDRecursiveSubviews)

- (NSArray *)allSubviews {
    NSMutableArray *allSubviews = [NSMutableArray arrayWithObject:self];
    NSArray *subviews = [self subviews];
    for (NSView *view in subviews) {
        [allSubviews addObjectsFromArray:[view allSubviews]];
    }
    return [[allSubviews copy] autorelease];
}

@end

С помощью быстрого файла с перьями, который я создал с иерархией представлений, он напечатал следующее:

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

Одно замечание, вызывающее беспокойство, которое я должен добавить, заключается в том, что мне неясно, как это может быть полезно, кроме как в качестве инструмента отладки. Но даже тогда, вероятно, есть более простые способы сделать что-то.

person NSGod    schedule 27.03.2011
comment
Работает и для iOS: NS - ›UI. - person clozach; 28.08.2012
comment
Не работает с ARC: *** - [NSMutableArray addObjectsFromArray:]: аргумент массива не является NSArray - person Sparky; 26.04.2018
comment
Более простой способ сделать это - просто добавить свои представления в NSMutableArray и сделать этот NSMutableArray доступным в более широкой области. Это тоже намного эффективнее. - person Sparky; 26.04.2018

Есть личное сообщение, которое можно отправить на NSView, чтобы распечатать иерархию управления.

[NSView _subtreeDescription] дает вам всю иерархию NSView, то есть его дочерние элементы и их дочерние элементы.

person Samit    schedule 02.11.2012

Если я вас правильно понял, вам придется создать метод, который рекурсивно вызывает сам себя. Что-то вроде этого:

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

чтобы узнать ваше мнение. (И не забывайте управлять памятью, если вы не используете сборщик мусора.)

person Enchilada    schedule 27.03.2011
comment
Рекурсивный метод никогда не закончится так, как вы его спроектировали. - person Moshe; 27.03.2011
comment
Спасибо, хотя это будет повторяться бесконечно до переполнения стека, я добавлю базовый случай. Но так ли это лучший способ? - person user635064; 27.03.2011
comment
@Enchilada: Не забудьте освободить массив. @ user635064: Нет, он не будет повторяться бесконечно. Это могло бы быть, если бы вы добавили представление как подпредставление одного из его собственных подвидов, но тогда это был бы замкнутый цикл, недоступный из contentView окна. - person Peter Hosey; 27.03.2011
comment
@Enchilada: Настоящая проблема этого метода не в том, что он повторяется бесконечно, а в том, что вы перечисляете изменяемый массив. - person Peter Hosey; 27.03.2011
comment
Почему эта рекурсия не повторяется бесконечно? Пока нет явного оператора return (или аналогичного оператора управления), метод будет рекурсивно повторяться бесконечно. - person Moshe; 27.03.2011
comment
@Moshe: он будет рекурсивно повторяться только до тех пор, пока в иерархии представлений есть представления. Если вы не можете создать файл пера или программно создать иерархию представлений, которая имеет бесконечное количество подпредставлений, само действие которых приведет к первой бесконечной рекурсии, а не к той, которая возвращается из этого метода. - person NSGod; 27.03.2011
comment
Ба, не могу редактировать мой предыдущий комментарий. Вот вариант получше: @Moshe: он будет рекурсивно повторяться только до тех пор, пока в иерархии представлений есть представления. Таким образом, нет бесконечной рекурсии, если вы не можете создать файл пера или программно создать иерархию представлений, которая имеет бесконечное количество подпредставлений (само действие которых приведет к первой бесконечной рекурсии, а не к любому, встречающемуся при вызове этого метода). - person NSGod; 27.03.2011
comment
Почему это не повторяется бесконечно? Каждый звонок вызывает себя, этому нет конца. Вы никогда не дойдете до второго представления, сколько бы их ни было. Кроме того, почему бы не использовать метод subviews? Он работает и встроен в SDK. - person Moshe; 27.03.2011
comment
@Moshe: в конечном итоге - возьмем, например, NSButton - когда он вызывает этот метод, [self subviews] будет пустым массивом, в который не будет повторяться рекурсия, и позволит оператору for/in перейти к следующему элементу. - person NSGod; 27.03.2011
comment
@Moshe: представление - это не часть самого себя. - person Peter Hosey; 28.03.2011
comment
Я бы предложил переименовать локальные "subviews" во что-то другое, например collectedViews, чтобы избежать путаницы. - person Rob van der Veer; 28.07.2013

Не уверен в бесконечной рекурсии в других ответах, но вы определенно не можете изменить массив, пока вы выполняете быстрое перечисление. Вот метод, который я написал для 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>>"
)
person smlxl    schedule 15.05.2012

Вышеупомянутое решение не будет работать, если в представлениях есть представления. Нам потребуется рекурсивное решение, как показано ниже.

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

person barryjones    schedule 28.02.2015

person    schedule
comment
OP искал подпредставления и их подпредставления (и т. Д., Я полагаю). - person Monolo; 14.07.2012