Wie liste ich alle Unteransichten in einem uiviewcontroller in iOS auf?


86

Ich möchte alle Unteransichten in a auflisten UIViewController. Ich habe es versucht self.view.subviews, aber nicht alle Unteransichten werden aufgelistet, zum Beispiel werden die Unteransichten in der UITableViewCellnicht gefunden. Irgendeine Idee?


Sie können alle Unteransichten durch rekursive Suche herausfinden. dh überprüfen Unteransicht hat Unteransichten ..
Mahesh

Antworten:


171

Sie müssen die Unteransichten rekursiv iterieren.

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

Wie von @Greg Meletic kommentiert, können Sie die COUNT CHECK LINE oben überspringen.


20
Technisch gesehen müssen Sie nicht überprüfen, ob die Anzahl der Unteransichten 0 beträgt.
Greg Maletic

5
NSLog (@ "\ n% @", [(id) self.view performSelector: @selector (recursiveDescription)]); Diese Zeile druckt das gleiche Ergebnis wie Ihre!
Hemang

Wenn Sie hier sind, überprüfen recursiveDescriptionSie bitte
Felipe Sabino

Hier ist meine Verbesserung der @ EmptyStack-Lösung. Es zeigt den Klassennamen und die Rahmenkoordinaten rekursiv eingerückt
Pierre de LESPINAY

35

Die in xcode / gdb integrierte Methode zum Speichern der Ansichtshierarchie ist nützlich - recursiveDescription ( http://developer.apple.com/library/ios/#technotes/tn2239/_index.html)

Es gibt eine vollständigere Ansichtshierarchie aus, die Sie möglicherweise nützlich finden:

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

Wo soll ich den Po [_myToolbar recursiveDescription]in meinen Code oder anderswo setzen?
19.

1
@ WildPointer Er spricht über die Konsole. Sie benötigen irgendwo einen Haltepunkt, um dies zu tun. po =
Druckobjekt

1
+1 Apple empfiehlt diesen Ansatz (gemäß der Sitzung Hidden Gems in Cocoa und Cocoa Touch von WWDC 2013, Tipp 5).
Slipp D. Thompson

@ पवन siehe meine Antwort hier . Ich habe diese Antwort umgeschrieben.
Honig

16

Elegante rekursive Lösung in Swift:

extension UIView {

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

}

Sie können subviewsRecursive () in jedem UIView aufrufen:

let allSubviews = self.view.subviewsRecursive()

13

Sie müssen rekursiv drucken, diese Methode auch Registerkarten basierend auf der Tiefe der Ansicht

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

}

12

Hier ist die schnelle Version

 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

Ich bin ein bisschen zu spät zur Party, aber eine etwas allgemeinere Lösung:

@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

Wenn Sie nur ein Array von UIViews wünschen, ist dies eine Einzeiler-Lösung ( Swift 4+ ):

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

Danke für diese Lösung! Ich habe Stunden damit verbracht, es herauszufinden, aber dann hat mich dein Beitrag gerettet!
user2525211

5

Ich benutze diesen Weg:

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

im UIViewController.


4

Einzelheiten

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

Lösung

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

Verwendung

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

Ergebnis

Geben Sie hier die Bildbeschreibung ein


3

In meiner Hinsicht ist die Kategorie oder Erweiterung von UIView viel besser als andere und rekursiv ist der entscheidende Punkt, um alle Unteransichten zu erhalten

Mehr erfahren:

https://github.com/ZhipingYang/XYDebugView

Geben Sie hier die Bildbeschreibung ein

Ziel 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

Beispiel

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: $0) }
        }
        getSubview(view: self)
        return all
    }
}

Beispiel

let views = viewController.view.recurrenceAllSubviews()

Verwenden Sie direkt die Sequenzfunktion , um alle Unteransichten abzurufen

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

Der Grund, warum die Unteransichten in einer UITableViewCell nicht gedruckt werden, liegt darin, dass Sie alle Unteransichten in der obersten Ebene ausgeben müssen. Die Unteransichten der Zelle sind nicht die direkten Unteransichten Ihrer Ansicht.

Um die Unteransichten der UITableViewCell abzurufen, müssen Sie ermitteln, welche Unteransichten zu einer UITableViewCell gehören (mithilfe von isKindOfClass: ) in Ihrer Druckschleife gehören, und dann die Unteransichten durchlaufen

Bearbeiten: Dieser Blog-Beitrag zu Easy UIView Debugging kann möglicherweise hilfreich sein


2

Ich habe eine Kategorie geschrieben, um alle Ansichten eines View Controllers aufzulisten, die von den zuvor veröffentlichten Antworten inspiriert wurden.

@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

Um alle Ansichten durch einen View - Controller gehalten, gerade NSLog("%@",[self listOfSubviews]), was selfbedeutet , die View - Controller selbst. Obwohl es nicht sehr effizient ist.

Außerdem können Sie NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);das Gleiche tun, und ich denke, es ist effizienter als meine Implementierung.


2

Einfaches Swift-Beispiel:

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

1

Sie könnten einen ausgefallenen Array-Trick ausprobieren, wie:

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

Nur eine Codezeile. Natürlich müssen Sie möglicherweise Ihre Methode anpassen printAllChildrenOfView, um keine Parameter zu übernehmen oder eine neue Methode zu erstellen.


1

Swift 2.0 kompatibel

Hier eine rekursive Methode, um alle Unteransichten einer generischen Ansicht zu erhalten:

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

So können Sie überall anrufen:

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

1

Geben Sie hier die Bildbeschreibung ein

Kürzeste Lösung

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

Ergebnis

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

Anmerkungen

  • Wie Sie sehen können, listet diese Methode die Unteransichten nicht rekursiv auf. Sehen Sie einige der anderen Antworten dafür.

1

Dies ist eine Umschreibung dieser Antwort:

Sie müssen zuerst den Zeiger / Verweis auf das Objekt erhalten, mit dem Sie alle Unteransichten drucken möchten. Manchmal fällt es Ihnen leichter, dieses Objekt zu finden, indem Sie über seine Unteransicht darauf zugreifen. Wie po someSubview.superview. Dies gibt Ihnen so etwas wie:

Optional<UIView>
   some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp ist Ihr App-Name
  • WhatsNewView ist der Typ Ihressuperview
  • 0x7f91747c71f0 ist der Zeiger auf die Übersicht.

Um die SuperView zu drucken, müssen Sie Haltepunkte verwenden.


Um diesen Schritt auszuführen, klicken Sie einfach auf "Debug-Hierarchie anzeigen". Keine Haltepunkte erforderlich

Geben Sie hier die Bildbeschreibung ein

Dann könnten Sie leicht tun:

po [0x7f91747c71f0 recursiveDescription]

was für mich so etwas wie zurückgegeben hat:

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

Wie Sie erraten haben müssen, hat meine Übersicht 4 Unteransichten:

  • eine stackView (die stackView selbst hat ein Bild und eine andere stackView (diese stackView hat 2 benutzerdefinierte Beschriftungen))
  • eine Textansicht
  • eine Sicht
  • ein Knopf

Dies ist für mich ziemlich neu, hat mir jedoch geholfen, die Frames (und den Text und den Typ) meiner Ansichten zu debuggen. Eine meiner Unteransichten wurde nicht auf dem Bildschirm angezeigt, daher wurde recursiveDescription verwendet, und mir wurde klar, dass die Breite meiner Unteransichten 0... war. Daher habe ich die Einschränkungen korrigiert und die Unteransicht wurde angezeigt.


0

Alternativ, wenn Sie ein Array aller Unteransichten (und verschachtelten Unteransichten) von einer UIView-Erweiterung zurückgeben möchten:

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-Version:

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

Wenn Sie alternativ alle Unteransichten eines bestimmten Typs finden möchten, den ich verwende:

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

Ich habe es in einer Kategorie gemacht, in der ich UIVieweinfach die Funktion aufrufe, die den Index übergibt, um sie mit einem schönen Baumformat zu drucken. Dies ist nur eine weitere Option der Antwort von James Webster .

#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++];
        }
    }
}

Ich hoffe, es hilft :)


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 behalten die Hierarchie der Ansichten bei. Um Unteransichten von uitableviewcell zu erhalten, müssen Sie wie folgt vorgehen.

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.