İOS'ta bir uiviewcontroller'deki tüm alt görünümleri nasıl listeleyebilirim?


87

Tüm alt görünümleri bir UIViewController. Denedim self.view.subviews, ancak tüm alt görünümler listelenmedi, örneğin içindeki alt UITableViewCellgörünümler bulunamadı. Herhangi bir fikir?


Yinelemeli aramayla tüm alt görünümleri bulabilirsiniz. yani alt görünümde alt görünümler var ..
Mahesh

Yanıtlar:


172

Alt görünümleri yinelemeli olarak yinelemeniz gerekir.

- (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 tarafından yorumlandığı gibi, yukarıdaki COUNT KONTROL HATTI'nı atlayabilirsiniz.


20
Teknik olarak, alt görüntüleme sayısının 0 olup olmadığını kontrol etmenize gerek yok.
Greg Maletic

5
NSLog (@ "\ n% @", [(id) self.view performSelector: @selector (recursiveDescription)]); Bu satır, sizinkiyle aynı sonucu yazdırır!
Hemang

recursiveDescriptionBuradaysanız , LÜTFEN @natbro - stackoverflow.com/a/8962824/429521
Felipe Sabino

İşte benim @EmptyStack çözümündeki geliştirmem . Sınıf adını ve çerçeve koordinatlarını yinelemeli olarak girintili olarak gösterir
Pierre de LESPINAY

35

Görünüm hiyerarşisini dökmenin xcode / gdb yerleşik yolu kullanışlıdır - http://developer.apple.com/library/ios/#technotes/tn2239/_index.html uyarınca recursiveDescription

Yararlı bulabileceğiniz daha eksiksiz bir görünüm hiyerarşisi sağlar:

> 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]kodunu kodumun neresine veya başka bir yere koyacağım .
पवन

1
@WildPointer Konsol hakkında konuşuyor. Bunu yapmak için bir yerde bir kesme noktasına ihtiyacınız var. po = nesneyi yazdır
smileBot

1
+1 Apple bu yaklaşımı önermektedir (WWDC 2013'ün Kakao ve Kakao Dokunuşta Gizli Mücevherler oturumu, 5. ipucu).
Slipp D. Thompson

@ पवन cevabımı burada görün . Bu cevabı yeniden yazdım.
Honey

17

Swift'de zarif özyinelemeli çözüm:

extension UIView {

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

}

Herhangi bir UIView'de subviewsRecursive () çağırabilirsiniz:

let allSubviews = self.view.subviewsRecursive()

13

Özyinelemeli olarak yazdırmanız gerekir, bu yöntem aynı zamanda görünümün derinliğine göre sekmeler de oluşturur.

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

İşte hızlı versiyon

 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

Partiye biraz geç kaldım, ancak biraz daha genel bir çözüm:

@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

Tek istediğiniz bir dizi UIViewdiziyse, bu tek satırlık bir çözümdür ( Swift 4+ ):

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

bu çözüm için teşekkürler! Anlamak için saatler harcadım ama sonra gönderiniz beni kurtardı!
user2525211

5

Bu şekilde kullanıyorum:

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

UIViewController'da.


4

Detaylar

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

Çözüm

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

Kullanım

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

Sonuç

görüntü açıklamasını buraya girin


3

Benim açımdan, UIView kategorisi veya uzantısı diğerlerinden çok daha iyi ve özyinelemeli, tüm alt görünümleri elde etmenin kilit noktasıdır.

daha fazla bilgi edin:

https://github.com/ZhipingYang/XYDebugView

görüntü açıklamasını buraya girin

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

misal

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

misal

let views = viewController.view.recurrenceAllSubviews()

doğrudan, tüm alt görünümleri almak için sıra işlevini kullanın

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

Bir UITableViewCell'deki alt görüntülemelerin yazdırılmamasının nedeni, üst düzeydeki tüm alt görüntülerin çıktısını almanız gerekmesidir. Hücrenin alt görünümleri, görünümünüzün doğrudan alt görünümleri değildir.

UITableViewCell'in alt görünümlerini elde etmek için isKindOfClass:, yazdırma döngünüzde hangi alt görünümlerin bir UITableViewCell'e ait olduğunu (kullanarak ) belirlemeniz ve ardından alt görünümleri arasında döngü oluşturmanız gerekir.

Düzenleme: Easy UIView Hata Ayıklama hakkındaki bu blog gönderisi potansiyel olarak yardımcı olabilir


2

Daha önce gönderilen cevaplardan esinlenen bir görünüm denetleyicisinin sahip olduğu tüm görüşleri listelemek için bir kategori yazdım.

@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

Bir görünüm denetleyicisi tarafından tutulan tüm görünümleri listelemek için NSLog("%@",[self listOfSubviews]), bu yalnızca selfgörünüm denetleyicisinin kendisi anlamına gelir. Etkili çıkmasa da.

Artı, NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);aynı şeyi yapmak için kullanabilirsiniz ve bence uygulamamdan daha verimli.


2

Basit Swift örneği:

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

1

Süslü bir dizi numarası deneyebilirsiniz, örneğin:

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

Sadece bir satır kod. Elbette, yönteminizi printAllChildrenOfViewherhangi bir parametre almamak veya yeni bir yöntem yapmak için ayarlamanız gerekebilir .


1

Swift 2.0 uyumlu

Burada, genel bir görünümün tüm alt görünümlerini elde etmek için yinelemeli bir yöntem:

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

Böylece her yeri şu şekilde arayabilirsiniz:

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

1

görüntü açıklamasını buraya girin

En kısa çözüm

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

Sonuç

UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide

Notlar

  • Gördüğünüz gibi, bu yöntem alt görünümleri yinelemeli olarak listelemiyor. Bunun için bazı diğer cevaplara bakın.

1

Bu, bu cevabın yeniden yazılması :

Önce, tüm alt görünümlerini yazdırmayı düşündüğünüz nesnenin işaretçisini / referansını almalısınız. Bazen alt görünümünden erişerek o nesneyi bulmayı daha kolay bulabilirsiniz. Beğen po someSubview.superview. Bu size şöyle bir şey verecektir:

Optional<UIView>
  ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
  • FaceBookApp olan senin uygulaması adı
  • WhatsNewView olan tip listenizdekilerdensuperview
  • 0x7f91747c71f0 süpervizörün işaretçisidir.

SuperView'ı yazdırmak için kesme noktaları kullanmanız gerekir.


Şimdi bu adımı yapmak için sadece 'hata ayıklama hiyerarşisini görüntüle' üzerine tıklayabilirsiniz. Kesme noktalarına gerek yok

görüntü açıklamasını buraya girin

O zaman kolayca yapabilirsin:

po [0x7f91747c71f0 recursiveDescription]

bu benim için şöyle bir şey döndürdü:

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

tahmin etmiş olabileceğiniz gibi, süpervizörümün 4 alt görünümü var:

  • bir stackView (stackView bir resme ve başka bir stackView'e sahiptir (bu stackView 2 özel etikete sahiptir )
  • bir textView
  • manzara
  • düğme

Bu benim için oldukça yeni, ancak görüşlerimin çerçevelerinde (ve metin ve tür) hata ayıklamama yardımcı oldu. Alt görünümlerimden biri ekranda görünmüyordu, bu yüzden recursiveDescription'ı kullandım ve subView'larımdan birinin genişliğinin ... olduğunu fark ettim 0... bu yüzden kısıtlamalarını düzelttim ve alt görünüm görünüyordu.


0

Alternatif olarak, bir UIView Uzantısından tüm alt görünümlerin (ve iç içe geçmiş alt görünümlerin) bir dizisini döndürmek isterseniz:

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 sürümü:

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

Alternatif olarak, kullandığım belirli bir türün tüm alt görünümlerini bulmak isterseniz:

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

UIViewGüzel bir ağaç biçiminde yazdırmak için dizini geçen işlevi çağırarak bir kategoride yaptım . Bu, James Webster tarafından gönderilen cevabın bir başka seçeneğidir .

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

Umut ediyorum bu yardım eder :)


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, görünümlerin mirasını korur. uitableviewcell'in alt görünümlerini almak için aşağıdaki gibi bir şey yapmanız gerekir.

 for (UIView *subView in self.view.subviews) {
      if ([subView isKindOfClass:[UITableView class]]) {
            for (UIView *tableSubview in subView.subviews) {
                .......
            }
      }
 }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.