webentwicklung-frage-antwort-db.com.de

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

Ich möchte alle Unteransichten in einem UIViewController auflisten. Ich habe es versucht self.view.subviews, aber nicht alle Unteransichten werden aufgelistet, z. B. werden die Unteransichten in UITableViewCell nicht gefunden. Irgendeine Idee?

83
user403015

Sie müssen die Unteransichten rekursiv durchlaufen.

- (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-Zeile oben überspringen.

171
EmptyStack

Die in xcode/gdb integrierte Methode zum Sichern der Ansichtshierarchie ist nützlich - recursiveDescription, per 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>>
35
natbro

Sie müssen rekursiv drucken. Diese Methode richtet sich auch nach 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];
    }

}
13
James Webster

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

Hier ist die Swift 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)
    }
}
11
Arkader

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

Wenn alles, was Sie wollen, ein Array von UIViews ist, ist dies eine einzeilige Lösung (Swift 4 +):

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

Ich benutze diesen Weg:

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

im UIViewController.

5
Graphite

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

enter image description here

4

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

erfahren Sie mehr:

https://github.com/ZhipingYang/XYDebugView

enter image description here

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 }
3
Zhiping Yang

Ich habe eine Kategorie geschrieben, um alle Ansichten aufzulisten, die von einem Ansichtscontroller gehalten werden, der von den zuvor veröffentlichten Antworten inspiriert wurde.

@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 aufzulisten, die von einem Ansichtscontroller gehalten werden, geben Sie einfach NSLog("%@",[self listOfSubviews]) ein, wobei self den Ansichtscontroller selbst bedeutet. Obwohl es nicht effizient zu beenden ist.

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

2
WeZZard

Einfaches Swift Beispiel:

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

Der Grund, warum die Unteransichten in einer UITableViewCell nicht gedruckt werden, besteht 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 zu erhalten, müssen Sie in Ihrer Druckschleife ermitteln, welche Unteransichten zu einer UITableViewCell gehören (mit isKindOfClass:), Und dann die Unteransichten durchlaufen

Bearbeiten: Dieser Blog-Beitrag auf Easy UIView Debugging kann möglicherweise helfen

2
Suhail Patel

enter image description here

Kürzeste Lösung

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

Ergebnis

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

Notizen

  • Wie Sie sehen, listet diese Methode die Unteransichten nicht rekursiv auf. Sehen Sie sich einige der anderen Antworten dazu an.
1
Suragch

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 auf diese Weise anrufen:

let view = FooController.view
let subviews = view.subviewsList()
1
Luca Davanzo

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

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

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

1
johnbakers

Dies ist eine Umschreibung von this answer:

Sie müssen zuerst den Zeiger/Verweis auf das Objekt erhalten, das Sie zum Drucken aller seiner Unteransichten verwenden möchten. Manchmal fällt es Ihnen möglicherweise leichter, dieses Objekt zu finden, indem Sie über seine Unteransicht darauf zugreifen. Wie po someSubview.superview. Dies gibt Ihnen etwas wie:

Optional<UIView>
  ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp ist dein App-Name
  • WhatsNewView ist der Typ Ihres superview
  • 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 Notwendigkeit für Haltepunkte

enter image description here

Dann könnten Sie leicht tun:

po [0x7f91747c71f0 recursiveDescription]

was für mich so etwas 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 sich sicher gedacht haben, hat mein Überblick 4 Unteransichten:

  • ein stackView (das stackView selbst hat ein Bild und ein anderes stackView (dieses stackView hat 2 custom labels))
  • eine Textansicht
  • eine Sicht
  • ein Knopf

Dies ist ziemlich neu für mich, 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, also wurde recursiveDescription verwendet und mir wurde klar, dass die Breite meiner Unteransichten 0 War. Also korrigierte ich die Einschränkungen und die Unteransicht wurde angezeigt.

1
Honey
- (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;
}
0
sarra srairi

Alternativ, wenn Sie ein Array aller Unteransichten (und verschachtelter 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
Adam Johns

Eine C # 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 suchen möchten, verwende ich:

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

Ich habe es in einer Kategorie von UIView gemacht, rufe einfach die Funktion auf, die den Index übergibt, um sie mit einem Nice-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
valbu17