Як перерахувати всі підзаписи в контролері uiview в iOS?


86

Я хочу перерахувати всі підпрограми в UIViewController. Я намагався self.view.subviews, але не всі підпрограми перераховані, наприклад, підпрограми в UITableViewCellне знайдені. Будь-яка ідея?


Ви можете дізнатись усі підпрограми за допомогою рекурсивного пошуку. тобто перевірити, що підпрогляд має підпрограми ..
Махеш,

Відповіді:


171

Ви повинні рекурсивно повторювати допоміжні подання.

- (void)listSubviewsOfView:(UIView *)view {

    // Get the subviews of the view
    NSArray *subviews = [view subviews];

    // Return if there are no subviews
    if ([subviews count] == 0) return; // COUNT CHECK LINE

    for (UIView *subview in subviews) {

        // Do what you want to do with the subview
        NSLog(@"%@", subview);

        // List the subviews of subview
        [self listSubviewsOfView:subview];
    }
}

Як прокоментував @Greg Meletic, ви можете пропустити РІВЕНЬ ПЕРЕВІРКИ КРАФІВ вище.


20
Технічно вам не потрібно перевіряти, чи є кількість переглядів рівним 0.
Грег Малетич

5
NSLog (@ "\ n% @", [(id) self.view performSelector: @selector (cursiveDescription)]); Цей рядок друкує той самий результат, що і ваш!
Hemang

Якщо ви тут, будь ласка , перевірте набагато простіше recursiveDescriptionвідповідь від @natbro - stackoverflow.com/a/8962824/429521
Феліпе Сабіна

Ось моє вдосконалення рішення @EmptyStack. Він показує назву класу та координати кадру рекурсивно з відступом
П’єр де ЛЕСПІН

35

Корисний вбудований xcode / gdb спосіб скидання ієрархії подання --cursiveDescription, за http://developer.apple.com/library/ios/#technotes/tn2239/_index.html

Він виводить більш повну ієрархію подання, яка може вам виявитися корисною:

> po [_myToolbar recursiveDescription]

<UIToolbarButton: 0xd866040; frame = (152 0; 15 44); opaque = NO; layer = <CALayer: 0xd864230>>
   | <UISwappableImageView: 0xd8660f0; frame = (0 0; 0 0); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xd86a160>>

Де розмістити po [_myToolbar recursiveDescription]в моєму коді або деінде.
वव

1
@WildPointer Він говорить про консоль. Для цього вам потрібно десь зупинитись. po = об'єкт друку
smileBot

1
+1 Apple рекомендує такий підхід (за сеансом прихованих самоцвітів у какао та какао WWDC 2013, підказка №5)
Сліпп Д. Томпсон,

@ पवन див. Мою відповідь тут . Я переписав цю відповідь.
Мед,

16

Елегантне рекурсивне рішення в Swift:

extension UIView {

    func subviewsRecursive() -> [UIView] {
        return subviews + subviews.flatMap { $0.subviewsRecursive() }
    }

}

Ви можете викликати subviewsRecursive () на будь-якому UIView:

let allSubviews = self.view.subviewsRecursive()

13

Вам потрібно друкувати рекурсивно, цей метод також здійснює вкладки на основі глибини перегляду

-(void) printAllChildrenOfView:(UIView*) node depth:(int) d
{
    //Tabs are just for formatting
    NSString *tabs = @"";
    for (int i = 0; i < d; i++)
    {
        tabs = [tabs stringByAppendingFormat:@"\t"];
    }

    NSLog(@"%@%@", tabs, node);

    d++; //Increment the depth
    for (UIView *child in node.subviews)
    {
        [self printAllChildrenOfView:child depth:d];
    }

}

13

Ось швидка версія

 func listSubviewsOfView(view:UIView){

    // Get the subviews of the view
    var subviews = view.subviews

    // Return if there are no subviews
    if subviews.count == 0 {
        return
    }

    for subview : AnyObject in subviews{

        // Do what you want to do with the subview
        println(subview)

        // List the subviews of subview
        listSubviewsOfView(subview as UIView)
    }
}

7

Я трохи запізнився на вечірку, але трохи більш загальне рішення:

@implementation UIView (childViews)

- (NSArray*) allSubviews {
    __block NSArray* allSubviews = [NSArray arrayWithObject:self];

    [self.subviews enumerateObjectsUsingBlock:^( UIView* view, NSUInteger idx, BOOL*stop) {
        allSubviews = [allSubviews arrayByAddingObjectsFromArray:[view allSubviews]];
                   }];
        return allSubviews;
    }

@end

6

Якщо все, що вам потрібно - це масив UIViews, це одне вкладене рішення ( Swift 4+ ):

extension UIView {
  var allSubviews: [UIView] {
    return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews }
  }
}

дякую за це рішення! Я витратив години, розгадуючи, але тоді ваш пост мене врятував!
користувач2525211,

5

Я використовую такий спосіб:

NSLog(@"%@", [self.view subviews]);

в UIViewController.


4

Деталі

  • Xcode 9.0.1, Swift 4
  • Xcode 10.2 (10E125), Swift 5

Рішення

extension UIView {
    private func subviews(parentView: UIView, level: Int = 0, printSubviews: Bool = false) -> [UIView] {
        var result = [UIView]()
        if level == 0 && printSubviews {
            result.append(parentView)
            print("\(parentView.viewInfo)")
        }

        for subview in parentView.subviews {
            if printSubviews { print("\(String(repeating: "-", count: level))\(subview.viewInfo)") }
            result.append(subview)
            if subview.subviews.isEmpty { continue }
            result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews)
        }
        return result
    }
    private var viewInfo: String { return "\(classForCoder), frame: \(frame))" }
    var allSubviews: [UIView] { return subviews(parentView: self) }
    func printSubviews() { _ = subviews(parentView: self, printSubviews: true) }
}

Використання

 view.printSubviews()
 print("\(view.allSubviews.count)")

Результат

введіть тут опис зображення


3

По-моєму, категорія або розширення UIView набагато краща за інші, а рекурсивний є ключовим моментом для отримання всіх підпроглядів

вчи більше:

https://github.com/ZhipingYang/XYDebugView

введіть тут опис зображення

Завдання-C

@implementation UIView (Recurrence)

- (NSArray<UIView *> *)recurrenceAllSubviews
{
    NSMutableArray <UIView *> *all = @[].mutableCopy;
    void (^getSubViewsBlock)(UIView *current) = ^(UIView *current){
        [all addObject:current];
        for (UIView *sub in current.subviews) {
            [all addObjectsFromArray:[sub recurrenceAllSubviews]];
        }
    };
    getSubViewsBlock(self);
    return [NSArray arrayWithArray:all];
}
@end

приклад

NSArray *views = [viewController.view recurrenceAllSubviews];

Свіфт 3.1

extension UIView {
    func recurrenceAllSubviews() -> [UIView] {
        var all = [UIView]()
        func getSubview(view: UIView) {
            all.append(view)
            guard view.subviews.count>0 else { return }
            view.subviews.forEach{ getSubview(view: $0) }
        }
        getSubview(view: self)
        return all
    }
}

приклад

let views = viewController.view.recurrenceAllSubviews()

безпосередньо використовуйте функцію послідовності, щоб отримати всі підпрогляди

let viewSequence = sequence(state: [viewController.view]) { (state: inout [UIView] ) -> [UIView]? in
    guard state.count > 0 else { return nil }
    defer {
        state = state.map{ $0.subviews }.flatMap{ $0 }
    }
    return state
}
let views = viewSequence.flatMap{ $0 }

2

Причиною того, що підпрогляди в UITableViewCell не друкуються, є те, що ви повинні вивести всі підзагляди на верхньому рівні. Підпрогляди комірки не є прямими підпрограмами вашого перегляду.

Для того, щоб отримати підпрогляди UITableViewCell, вам потрібно визначити, які підпрограми належать до UITableViewCell (використовуючи isKindOfClass: ) у вашому циклі друку, а потім

Редагувати: Цей допис у блозі про Easy UIView Налагодження може потенційно допомогти


2

Я написав категорію, щоб перерахувати всі погляди контролера перегляду, натхненні відповідями, розміщеними раніше.

@interface UIView (ListSubviewHierarchy)
- (NSString *)listOfSubviews;
@end

@implementation UIView (ListSubviewHierarchy)
- (NSInteger)depth
{
    NSInteger depth = 0;
    if ([self superview]) {
        deepth = [[self superview] depth] + 1;
    }
    return depth;
}

- (NSString *)listOfSubviews
{
    NSString * indent = @"";
    NSInteger depth = [self depth];

    for (int counter = 0; counter < depth; counter ++) {
        indent = [indent stringByAppendingString:@"  "];
    }

    __block NSString * listOfSubviews = [NSString stringWithFormat:@"\n%@%@", indent, [self description];

    if ([self.subviews count] > 0) {
        [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            UIView * subview = obj;
            listOfSubviews = [listOfSubviews stringByAppendingFormat:@"%@", [subview listOfSubviews]];
        }];
    }
    return listOfSubviews;
}
@end

Щоб перерахувати всі перегляди, що проводяться контролером перегляду, просто NSLog("%@",[self listOfSubviews]), якийself означає сам контролер перегляду. Хоча це не ефективно.

Крім того, ви можете використовувати NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);те саме, і я думаю, що це ефективніше, ніж моє впровадження.


2

Простий приклад Swift:

 var arrOfSub = self.view.subviews
 print("Number of Subviews: \(arrOfSub.count)")
 for item in arrOfSub {
    print(item)
 }

1

Ви можете спробувати химерний фокус із масивом, наприклад:

[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];

Лише один рядок коду. Звичайно, вам може знадобитися налаштувати свій метод, printAllChildrenOfViewщоб не брати жодних параметрів або створювати новий метод.


1

Сумісний із Swift 2.0

Ось рекурсивний метод для отримання всіх підпрограм загального подання:

extension UIView {
  func subviewsList() -> [UIView] {
      var subviews = self.subviews
      if subviews.count == 0 {
          return subviews + []
      }
      for v in subviews {
         subviews += v.listSubviewsOfView()
      }
      return subviews
  }
}

Тож ви можете дзвонити скрізь таким чином:

let view = FooController.view
let subviews = view.subviewsList()

1

введіть тут опис зображення

Найкоротше рішення

for subview in self.view.subviews {
    print(subview.dynamicType)
}

Результат

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

Примітки

  • Як бачите, цей метод не перелічує підвиди рекурсивно. Подивіться деякі інші відповіді на це.

1

Це переписування цієї відповіді:

Спочатку потрібно отримати вказівник / посилання на об’єкт, який ви збираєтесь надрукувати всі його підпрогляди. Іноді вам може бути простіше знайти цей об’єкт, отримавши доступ до нього через його підпрогляд. Подобається po someSubview.superview. Це дасть вам щось на зразок:

Optional<UIView>
   some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp - це ваше ім’я програми
  • WhatsNewView - це тип вашогоsuperview
  • 0x7f91747c71f0 - вказівник на суперпогляд.

Щоб надрукувати superView, потрібно використовувати точки зупинку.


Тепер, щоб зробити цей крок, ви можете просто натиснути на "ієрархію перегляду налагодження". Не потрібні точки зупинку

введіть тут опис зображення

Тоді ви можете легко зробити:

po [0x7f91747c71f0 recursiveDescription]

що для мене повернуло щось на зразок:

<FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
   | <UIStackView: 0x7f91747c75f0; frame = (45 60; 264 93); layer = <CATransformLayer: 0x610000230ec0>>
   |    | <UIImageView: 0x7f916ef38c30; frame = (10.6667 0; 243 58); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x61000003b840>>
   |    | <UIStackView: 0x7f91747c8230; frame = (44.6667 58; 174.667 35); layer = <CATransformLayer: 0x6100006278c0>>
   |    |    | <FacebookApp.CopyableUILabel: 0x7f91747a80b0; baseClass = UILabel; frame = (44 0; 86.6667 16); text = 'What's New'; gestureRecognizers = <NSArray: 0x610000c4a770>; layer = <_UILabelLayer: 0x610000085550>>
   |    |    | <FacebookApp.CopyableUILabel: 0x7f916ef396a0; baseClass = UILabel; frame = (0 21; 174.667 14); text = 'Version 14.0.5c Oct 05, 2...'; gestureRecognizers = <NSArray: 0x610000c498a0>; layer = <_UILabelLayer: 0x610000087300>>
   | <UITextView: 0x7f917015ce00; frame = (45 183; 264 403); text = '   •   new Adding new feature...'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x6100000538f0>; layer = <CALayer: 0x61000042f000>; contentOffset: {0, 0}; contentSize: {264, 890}>
   |    | <<_UITextContainerView: 0x7f9170a13350; frame = (0 0; 264 890); layer = <_UITextTiledLayer: 0x6080002c0930>> minSize = {0, 0}, maxSize = {1.7976931348623157e+308, 1.7976931348623157e+308}, textContainer = <NSTextContainer: 0x610000117b20 size = (264.000000,340282346638528859811704183484516925440.000000); widthTracksTextView = YES; heightTracksTextView = NO>; exclusionPaths = 0x61000001bc30; lineBreakMode = 0>
   |    |    | <_UITileLayer: 0x60800023f8a0> (layer)
   |    |    | <_UITileLayer: 0x60800023f3c0> (layer)
   |    |    | <_UITileLayer: 0x60800023f360> (layer)
   |    |    | <_UITileLayer: 0x60800023eca0> (layer)
   |    | <UIImageView: 0x7f9170a7d370; frame = (-39 397.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f4c0>>
   |    | <UIImageView: 0x7f9170a7d560; frame = (258.667 -39; 2.33333 36); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f5e0>>
   | <UIView: 0x7f916ef149c0; frame = (0 587; 354 0); layer = <CALayer: 0x6100006392a0>>
   | <UIButton: 0x7f91747a8730; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x610000639320>>
   |    | <UIButtonLabel: 0x7f916ef00a80; frame = (0 -5.66667; 0 16); text = 'See More Details'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x610000084d80>>

як ви вже здогадалися, мій суперпрегляд має 4 підпрограми:

  • stackView (сам stackView має зображення та ще один stackView (цей stackView має 2 власні мітки))
  • a textView
  • вид
  • кнопку

Це досить нове для мене, але допомогло мені налагодити рамки моїх поглядів (і текст, і тип). Один з моїх підпроектів не відображався на екрані, тому я використовував рекурсивний опис, і я зрозумів, що ширина мого одного з моїх підглядів був 0... так що я пішов, виправивши його обмеження, і підгляд з'явився.


0

Як варіант, якщо ви хочете повернути масив усіх підпоглядів (і вкладених підпроглядів) із розширення UIView:

func getAllSubviewsRecursively() -> [AnyObject] {
    var allSubviews: [AnyObject] = []

    for subview in self.subviews {
        if let subview = subview as? UIView {
            allSubviews.append(subview)
            allSubviews = allSubviews + subview.getAllSubviewsRecursively()
        }
    }

    return allSubviews
}

0

Версія AC # Xamarin:

void ListSubviewsOfView(UIView view)
{
    var subviews = view.Subviews;
    if (subviews.Length == 0) return;

    foreach (var subView in subviews)
    {
        Console.WriteLine("Subview of type {0}", subView.GetType());
        ListSubviewsOfView(subView);
    }
}

Як варіант, якщо ви хочете знайти всі підпрограми певного типу, я використовую:

List<T> FindViews<T>(UIView view)
{
    List<T> allSubviews = new List<T>();
    var subviews = view.Subviews.Where(x =>  x.GetType() == typeof(T)).ToList();

    if (subviews.Count == 0) return allSubviews;

       foreach (var subView in subviews)
       {
            allSubviews.AddRange(FindViews<T>(subView));
        }

    return allSubviews;

}

0

Я зробив це в категорії UIViewпросто викликати функцію, що передає індекс, щоб надрукувати їх у гарному деревному форматі. Це лише ще один варіант відповіді, опублікований Джеймсом Вебстером .

#pragma mark - Views Tree

- (void)printSubviewsTreeWithIndex:(NSInteger)index
{
    if (!self)
    {
        return;
    }


    NSString *tabSpace = @"";

    @autoreleasepool
    {
        for (NSInteger x = 0; x < index; x++)
        {
            tabSpace = [tabSpace stringByAppendingString:@"\t"];
        }
    }

    NSLog(@"%@%@", tabSpace, self);

    if (!self.subviews)
    {
        return;
    }

    @autoreleasepool
    {
        for (UIView *subView in self.subviews)
        {
            [subView printViewsTreeWithIndex:index++];
        }
    }
}

Сподіваюся, це допоможе :)


0
- (NSString *)recusiveDescription:(UIView *)view
{
    NSString *s = @"";
    NSArray *subviews = [view subviews];
    if ([subviews count] == 0) return @"no subviews";

    for (UIView *subView in subviews) {
         s = [NSString stringWithFormat:@"<%@; frame = (%f %f : %f %f) \n ",NSStringFromClass([subView class]), subView.frame.origin.x, subView.frame.origin.y ,subView.frame.size.width, subView.frame.size.height];  
        [self recusiveDescription:subView];
    }
    return s;
}

-1

self.view.subviews підтримує ієрархію поглядів. Щоб отримати підпрогляди uitableviewcell, вам потрібно зробити щось подібне нижче.

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.