UIView의 모든 하위보기, 하위보기 및 하위보기를 반복하는 방법


81

UIView의 모든 하위보기, 하위보기 및 하위보기를 어떻게 반복 할 수 있습니까?


7
정말 반복해야 할 필요가 있다면 받아 들여진 대답이 맞습니다. 단지 하나의 뷰를 찾고 있다면 태그를 달고 대신 viewWithTag를 사용하십시오. 고통을 덜어주세요! - developer.apple.com/library/ios/documentation/UIKit/Reference/...
노먼 H

답변:


122

재귀 사용 :

// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy;
@end

// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy
{
    NSLog(@"%@", self);
    for (UIView *subview in self.subviews)
    {
        [subview logViewHierarchy];
    }
}
@end

// In your implementation
[myView logViewHierarchy];

2
logViewHierarchy에 함수를 전달하는 방법이 있습니까? 그렇게하면 다른 시간에 귀하의 요구에 맞출 수 있습니다.
docchang 2011 년

2
@docchang : Obj-C 블록은 해당 사용 사례에 매우 적합합니다. 메서드에 블록을 전달하고 블록을 실행 한 다음 각 메서드 호출과 함께 계층 구조 아래로 전달합니다.
Ole Begemann 2011 년

좋은. 이 기술에 공백 들여 쓰기를 추가하는 스 니펫을 아래에 게시했습니다.
jaredsinclair

34

여기에 UIView 클래스에 대한 재귀와 래퍼 (카테고리 / 확장)를 사용하는 솔루션이 있습니다.

// UIView+viewRecursion.h
@interface UIView (viewRecursion)
- (NSMutableArray*) allSubViews;
@end

// UIView+viewRecursion.m
@implementation UIView (viewRecursion)
- (NSMutableArray*)allSubViews
{
   NSMutableArray *arr=[[[NSMutableArray alloc] init] autorelease];
   [arr addObject:self];
   for (UIView *subview in self.subviews)
   {
     [arr addObjectsFromArray:(NSArray*)[subview allSubViews]];
   }
   return arr;
}
@end

사용법 : 이제 모든 하위 뷰를 반복하고 필요에 따라 조작해야합니다.

//disable all text fields
for(UIView *v in [self.view allSubViews])
{
     if([v isKindOfClass:[UITextField class]])
     {
         ((UITextField*)v).enabled=NO;
     }
}

3
allSubViews함수에 메모리 누수가 있음을 유의하십시오 . 배열을 as [[[NSMutableArray alloc] init] autorelease]또는 as [NSMutableArray array](동일) 로 만들어야합니다 .
ivanzoid 2011 년

1
"UIView의 래퍼"는 실제로 "UIView의 범주"를 의미합니다.
Dimitris 2012-06-11

네, 디미트리 스, 카테고리를 의미했습니다.
RamaKrishna Chunduri


16

subviews뷰 자체를 포함하지 않고 모든 것을 제공하는 Swift 3의 솔루션 :

extension UIView {
var allSubViews : [UIView] {

        var array = [self.subviews].flatMap {$0}

        array.forEach { array.append(contentsOf: $0.allSubViews) }

        return array
    }
}

이 줄 "var array = [self.subviews] .flatMap {$ 0}"에서 ".flatMap {$ 0}"이 필요합니까?
Rahul Patel

@RahulPatel 은 하위 뷰를 nil호출 할 때 안전을 위해 배열에서 요소를 제거합니다 allSubViews.
ielyamani

12

생성 될 때 모든 것을 태그합니다. 그러면 하위보기를 쉽게 찾을 수 있습니다.

view = [aView viewWithTag:tag];


10

다음은 실제 뷰 루핑 및 중단 기능이있는 예입니다.

빠른:

extension UIView {

    func loopViewHierarchy(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        var stop = false
        block(self, &stop)
        if !stop {
            self.subviews.forEach { $0.loopViewHierarchy(block: block) }
        }
    }

}

전화 예 :

mainView.loopViewHierarchy { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}

역 루핑 :

extension UIView {

    func loopViewHierarchyReversed(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
        for i in stride(from: self.highestViewLevel(view: self), through: 1, by: -1) {
            let stop = self.loopView(view: self, level: i, block: block)
            if stop {
                break
            }
        }
    }

    private func loopView(view: UIView, level: Int, block: (_ view: UIView, _ stop: inout Bool) -> ()) -> Bool {
        if level == 1 {
            var stop = false
            block(view, &stop)
            return stop
        } else if level > 1 {
            for subview in view.subviews.reversed() {
            let stop = self.loopView(view: subview, level: level - 1, block: block)
                if stop {
                    return stop
                }
            }
        }
        return false
    }

    private func highestViewLevel(view: UIView) -> Int {
        var highestLevelForView = 0
        for subview in view.subviews.reversed() {
            let highestLevelForSubview = self.highestViewLevel(view: subview)
            highestLevelForView = max(highestLevelForView, highestLevelForSubview)
        }
        return highestLevelForView + 1
    }
}

전화 예 :

mainView.loopViewHierarchyReversed { (view, stop) in
    if view is UIButton {
        /// use the view
        stop = true
    }
}

목표 -C :

typedef void(^ViewBlock)(UIView* view, BOOL* stop);

@interface UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block;
@end

@implementation UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block {
    BOOL stop = NO;
    if (block) {
        block(self, &stop);
    }
    if (!stop) {
        for (UIView* subview in self.subviews) {
            [subview loopViewHierarchy:block];
        }
    }
}
@end

전화 예 :

[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) {
    if ([view isKindOfClass:[UIButton class]]) {
        /// use the view
        *stop = YES;
    }
}];

7

Ole Begemann의 도움으로. 블록 개념을 통합하기 위해 몇 줄을 추가했습니다.

UIView + HierarchyLogging.h

typedef void (^ViewActionBlock_t)(UIView *);
@interface UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction;
@end

UIView + HierarchyLogging.m

@implementation UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction {
    //view action block - freedom to the caller
    viewAction(self);

    for (UIView *subview in self.subviews) {
        [subview logViewHierarchy:viewAction];
    }
}
@end

ViewController에서 HierarchyLogging 카테고리 사용. 이제해야 할 일을 자유롭게 할 수 있습니다.

void (^ViewActionBlock)(UIView *) = ^(UIView *view) {
    if ([view isKindOfClass:[UIButton class]]) {
        NSLog(@"%@", view);
    }
};
[self.view logViewHierarchy: ViewActionBlock];

여기서 제안한 것과 매우 유사한 솔루션을 만든 것 같습니다. 다른 사람들이 유용하다고 생각할 수 있도록 github에 게시했습니다. 링크가있는 내 대답은 다음과 같습니다. stackoverflow.com/a/10440510/553394
Eric Goldberg

블록 내에서 재귀를 중지하는 방법을 어떻게 추가 할 수 있습니까? 특정 뷰를 찾기 위해 이것을 사용하고 있다고 가정하면, 일단 그것을 발견하면 거기서 멈추고 나머지 뷰 계층 구조 검색을 계속하지 않겠습니다.
Mic Pringle 2014 년

4

새로운 기능을 만들 필요가 없습니다. Xcode로 디버깅 할 때만하십시오.

뷰 컨트롤러에서 중단 점을 설정하고이 중단 점에서 앱을 일시 중지합니다.

빈 영역을 마우스 오른쪽 버튼으로 클릭하고 Xcode의 Watch 창에서 "식 추가 ..."를 누릅니다.

다음 줄을 입력하십시오.

(NSString*)[self->_view recursiveDescription]

값이 너무 길면 마우스 오른쪽 단추로 클릭하고 "...에 대한 설명 인쇄"를 선택하십시오. 콘솔 창에서 self.view의 모든 하위보기를 볼 수 있습니다. self.view의 하위보기를 보지 않으려면 self-> _ view를 다른 것으로 변경하십시오.

끝난! gdb가 없습니다!


'빈 영역'이 어디에 있는지 설명해 주시겠습니까? 나는 브레이크 포인트가 실행되면 Xcode의 창 주위를 시도했지만 어디에 입력 문자열을 찾을 수 없습니다
SundialSoft

4

다음은 재귀 코드입니다.

 for (UIView *subViews in yourView.subviews) {
    [self removSubviews:subViews];

}   

-(void)removSubviews:(UIView *)subView
{
   if (subView.subviews.count>0) {
     for (UIView *subViews in subView.subviews) {

        [self removSubviews:subViews];
     }
  }
  else
  {
     NSLog(@"%i",subView.subviews.count);
    [subView removeFromSuperview];
  }
}

도움이되는 솔루션과 시간을 절약 .. 고맙습니다 1 플러스 U에
브레인 스토밍 Technolabs

3

그건 그렇고, 나는 이런 종류의 작업을 돕기 위해 오픈 소스 프로젝트를 만들었습니다. 정말 쉽고 Objective-C 2.0 블록을 사용하여 계층 구조의 모든 뷰에서 코드를 실행합니다.

https://github.com/egold/UIViewRecursion

예:

-(void)makeAllSubviewsGreen
{
    [self.view runBlockOnAllSubviews:^(UIView *view) {

        view.backgroundColor = [UIColor greenColor];
    }];
}

2

다음은 위 의 Ole Begemann의 답변 에 대한 변형이며 , 계층 구조를 설명하기 위해 들여 쓰기를 추가합니다.

// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces;
@end

// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces {
    if (whiteSpaces == nil) {
        whiteSpaces = [NSString string];
    }
    NSLog(@"%@%@", whiteSpaces, self);

    NSString *adjustedWhiteSpaces = [whiteSpaces stringByAppendingFormat:@"    "];

    for (UIView *subview in self.subviews) {
        [subview logViewHierarchy:adjustedWhiteSpaces];
    }
}
@end

1

이 답변에 게시 된 코드는 모든 창과 모든보기 및 모든 하위보기를 순회합니다. 뷰 계층 구조의 출력물을 NSLog에 덤프하는 데 사용되었지만 뷰 계층 구조의 모든 순회를위한 기반으로 사용할 수 있습니다. 재귀 C 함수를 사용하여 뷰 트리를 탐색합니다.


0

몇 가지 뷰를 디버그하기 위해 얼마 전에 카테고리를 작성 했습니다 .

IIRC, 게시 된 코드가 작동하는 코드입니다. 그렇지 않은 경우 올바른 방향으로 안내합니다. 자기 책임 등으로 사용하십시오.


0

계층 구조 수준도 표시됩니다.

@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(int)level
{
    NSLog(@"%d - %@", level, self);
    for (UIView *subview in self.subviews)
    {
        [subview logViewHierarchy:(level+1)];
    }
}
@end

0

이 페이지를 먼저 찾았 으면 하지만 (어떤 이유로 든) 카테고리가 아닌 더 많은 코드 줄을 사용하여 비재 귀적으로 수행하려는 경우


0

재귀를 사용하는 모든 답변 (디버거 옵션 제외)이 범주를 사용했다고 생각합니다. 범주가 필요하지 않거나 원하지 않는 경우 인스턴스 메서드를 사용할 수 있습니다. 예를 들어 뷰 계층 구조의 모든 레이블 배열을 가져와야하는 경우 이렇게 할 수 있습니다.

@interface MyViewController ()
@property (nonatomic, retain) NSMutableArray* labelsArray;
@end

@implementation MyViewController

- (void)recursiveFindLabelsInView:(UIView*)inView
{
    for (UIView *view in inView.subviews)
    {
        if([view isKindOfClass:[UILabel class]])
           [self.labelsArray addObject: view];
        else
           [self recursiveFindLabelsInView:view];
    }
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.labelsArray = [[NSMutableArray alloc] init];
    [self recursiveFindLabelsInView:self.view];

    for (UILabel *lbl in self.labelsArray)
    {
        //Do something with labels
    }
}

-1

아래 메서드는 하나 이상의 가변 배열을 만든 다음 입력보기의 하위보기를 반복합니다. 이렇게하면 초기 하위보기를 추가 한 다음 해당 하위보기의 하위보기가 있는지 여부를 쿼리합니다. true이면 다시 자신을 호출합니다. 계층 구조의 모든보기가 추가 될 때까지 그렇게합니다.

-(NSArray *)allSubs:(UIView *)view {

    NSMutableArray * ma = [NSMutableArray new];
    for (UIView * sub in view.subviews){
        [ma addObject:sub];
        if (sub.subviews){
            [ma addObjectsFromArray:[self allSubs:sub]];
        }
    }
    return ma;
}

다음을 사용하여 전화 :

NSArray * subviews = [self allSubs:someView];

1
이 코드가 어떻게 작동하는지 설명하는 편집 링크를 사용하고 코드 만 제공하지 마십시오. 설명이 미래의 독자에게 도움이 될 가능성이 더 높기 때문입니다. 응답 방법을 참조하십시오 . 출처
Jed Fox

1
위의 내 의견을 참조하십시오.
Jed Fox
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.