Как перечислить все подвиды в uiviewcontroller в iOS?



Я хочу перечислить все подвиды в UIViewController. Я пытался self.view.subviews, но не все подвиды перечислены, например, подвиды в UITableViewCell не нашли. Есть идеи?

559   22  

22 ответов:

вы должны рекурсивно перебирать вложенные представления.

- (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, вы можете пропустить строку проверки количества выше.

встроенный способ Xcode/gdb для сброса иерархии представлений полезен -- recursiveDescription, per 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>>

вы должны печатать рекурсивно, этот метод также вкладки на основе глубины представления

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

}

вот версия swift

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

Я немного опоздала на вечеринку, но немного более общее решение:

@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

элегантное рекурсивное решение в Swift:

extension UIView {

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

}

вы можете вызвать subviewsRecursive () на любом UIView:

let allSubviews = self.view.subviewsRecursive()

Я использую таким образом:

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

в UIViewController.

Если все, что вы хотите-это массив UIViews, это одно линейное решение (Swift 4+):

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

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

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

редактировать: это сообщение в блоге на Простая Отладка UIView может потенциально помогите

Я написал категорию, чтобы перечислить все представления, удерживаемые контроллером представления, который вдохновлен ответами, опубликованными ранее.

@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)]); сделать то же самое, и я думаю, что это более эффективно, чем моя реализация.

простой пример Swift:

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

на мой взгляд, категория или расширение UIView намного лучше, чем другие и рекурсивный является ключевым моментом, чтобы получить все подвиды

подробнее:

https://github.com/ZhipingYang/XYDebugView

С

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

Swift 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: ) }
        }
        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{ .subviews }.flatMap{  }
    }
    return state
}
let views = Array(viewSequence).flatMap{  }

подробности

xCode 9.0.1, Swift 4

решение

extension UIView {

    private var viewInfo: String {
        return "\(classForCoder), frame: \(frame))"
    }

    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.count != 0 {
                result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews)
            }
        }
        return result
    }

    var allSubviews: [UIView] {
        return subviews(parentView: self)
    }

    func printSubviews() {
        _ = subviews(parentView: self, printSubviews: true)
    }

}

использование

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

результат

enter image description here

вы можете попробовать причудливый трюк с массивом, например:

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

только одна строка кода. Конечно, вам может потребоваться настроить свой метод printAllChildrenOfView чтобы не принимать никаких параметров или сделать новый метод.

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

это переписывание этой ответ:

сначала вы должны получить указатель / ссылку на объект, который вы собираетесь распечатать все его подвиды. Иногда вам может быть проще найти этот объект, обратившись к нему через его подвиды. Как po someSubview.superview. Это даст вам что-то вроде:

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

для печати супервизора необходимо использовать точки останова.


теперь, чтобы сделать этот шаг, вы можете просто нажать на "просмотр иерархии отладки". Нет необходимости в точках останова

enter image description here

потом вы могли бы легко сделать:

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 custom ярлыки))
  • textView
  • вид
  • на

это довольно ново для меня, но помогло мне отладить фреймы моих представлений (а также текст и тип). Один из моих подвидов не показывался на экране, поэтому использовал recursiveDescription, и я понял, что ширина моего одного из моих подвидов была 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
}

enter image description here

самое короткое решение

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

результат

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

Примечания

  • как вы можете видеть, этот метод не перечисляет подвиды рекурсивно. См. некоторые другие ответы для этого.

версия C# 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;

}

Я сделал это в категории 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++];
        }
    }
}

надеюсь, это поможет :)

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

self.вид.подвиды поддерживают наследственность views.To получить подвиды uitableviewcell вы должны сделать что-то вроде ниже.

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }

Comments

    Ничего не найдено.