iOS에서 uiviewcontroller의 모든 하위보기를 나열하는 방법은 무엇입니까?


87

모든 하위 뷰를 UIViewController. 시도 self.view.subviews했지만 모든 하위보기가 나열 UITableViewCell되지 않았습니다. 예를 들어의 하위보기를 찾을 수 없습니다. 어떤 생각?


재귀 검색으로 모든 하위보기를 찾을 수 있습니다. 즉, 하위보기에 하위보기가 있는지 확인합니다.
Mahesh

답변:


172

하위 뷰를 재귀 적으로 반복해야합니다.

- (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이 언급했듯이 위의 COUNT CHECK LINE을 건너 뛸 수 있습니다.


20
기술적으로, 당신은 서브 뷰 카운트가 0인지 확인 할 필요가 없습니다
그렉 맬틱

5
NSLog (@ "\ n % @", [(id) self.view performSelector : @selector (recursiveDescription)]); 이 줄은 당신과 같은 결과를 인쇄합니다!
Hemang 2014 년

당신이 여기 있다면, 훨씬 더 간단 확인하시기 바랍니다 recursiveDescription@natbro에서 답을 - stackoverflow.com/a/8962824/429521
펠리페 비노

여기 내입니다 개선 @EmptyStack 솔루션은. 이 클래스 이름 및 프레임 좌표가 반복적으로 들여 쓰기를 보여줍니다
피에르 드 LESPINAY

35

보기 계층 구조를 덤프하는 xcode / gdb 기본 제공 방법이 유용합니다-recursiveDescription, 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>>

[_myToolbar recursiveDescription]내 코드 또는 다른 곳에 po를 넣을 위치.
पवन dec.

1
@WildPointer 그는 콘솔에 대해 이야기하고 있습니다. 이렇게하려면 어딘가에 중단 점이 필요합니다. po = print object
smileBot 2013 년

1
+1 Apple은이 접근 방식을 권장합니다 (WWDC 2013의 Cocoa 및 Cocoa Touch 세션에서 숨겨진 보석, 팁 # 5).
Slipp D. Thompson 2013

@ पवन 여기에서 내 대답을 참조 하십시오 . 이 답변을 다시 작성했습니다.
Honey

17

Swift의 우아한 재귀 솔루션 :

extension UIView {

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

}

모든 UIView에서 subviewsRecursive ()를 호출 할 수 있습니다.

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

이 솔루션에 감사드립니다! 나는 알아내는 데 몇 시간을 보냈지 만 당신의 게시물은 나를 구했습니다!
user2525211

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의 하위보기를 얻으 isKindOfClass:려면 인쇄 루프에서 UITableViewCell (사용 )에 속하는 하위보기를 확인한 다음 하위보기를 반복해야합니다.

편집 : 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 개의 사용자 정의 레이블이 있음))
  • textView
  • 관점
  • 단추

이것은 나에게 상당히 새롭지 만 내 뷰의 프레임 (및 텍스트 및 유형)을 디버그하는 데 도움이되었습니다. 내 하위보기 중 하나가 화면에 표시되지 않아 recursiveDescription을 사용했고 내 하위보기 중 하나의 너비가 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인덱스를 전달하는 함수를 호출하여 멋진 트리 형식으로 인쇄 하는 범주에서 수행했습니다 . 이것은 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++];
        }
    }
}

나는 그것이 도움이되기를 바랍니다 :)


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) {
                .......
            }
      }
 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.