programing

UIView의 모든 하위 보기와 하위 보기 및 하위 보기를 순환하려면 어떻게 해야 합니까?

codeshow 2023. 7. 31. 22:00
반응형

UIView의 모든 하위 보기와 하위 보기 및 하위 보기를 순환하려면 어떻게 해야 합니까?

UIView의 모든 하위 보기와 하위 보기 및 하위 보기를 순환하려면 어떻게 해야 합니까?

재귀 사용:

// 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];

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

Swift의 또 다른 구현은 다음과 같습니다.

extension UIView {
    var allSubviews: [UIView] {
        return self.subviews.flatMap { [$0] + $0.allSubviews }
    }
}

Swift 3에서 모든 기능을 제공하는 솔루션subviews보기 자체를 포함하지 않고:

extension UIView {
var allSubViews : [UIView] {

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

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

        return array
    }
}

모든 것을 만들 때 태그를 붙입니다.그러면 어떤 서브뷰든 쉽게 찾을 수 있습니다.

view = [aView viewWithTag:tag];

디버거를 통해 흥미로운 방법을 찾았습니다.

http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/

이 Apple Technote를 참조합니다.

https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT

디버거가 일시 중지되었는지 확인하고(수동으로 일시 중지 중단 지점을 설정), 다음을 요청할 수 있습니다.recursiveDescription.

다음은 실제 보기 루프 및 중단 기능을 사용한 예제입니다.

스위프트:

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

올레 베게만의 도움으로.저는 블록 개념을 통합하기 위해 몇 줄을 추가했습니다.

UIView+계층 로깅.h

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

UIView+계층 로깅.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

View 컨트롤러에서 계층 로깅 범주 사용.당신은 이제 당신이 해야 할 일에 대한 자유를 갖게 되었습니다.

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

새 함수를 만들 필요가 없습니다.Xcode로 디버깅할 때만 하면 됩니다.

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

빈 영역을 마우스 오른쪽 단추로 클릭하고 "식 추가...Xcode의 Watch 창에서.

다음 줄 입력:

(NSString*)[self->_view recursiveDescription]

값이 너무 길면 마우스 오른쪽 단추를 누른 후 "설명 인쇄..."를 선택합니다. 자신의 모든 하위 보기가 표시됩니다.콘솔 창에 표시됩니다.self.view의 하위 뷰를 보고 싶지 않으면 self->_view를 다른 것으로 변경합니다.

됐어요! 땡땡이!

다음은 재귀 코드입니다.

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

그런데, 저는 이런 종류의 작업을 돕기 위해 오픈 소스 프로젝트를 만들었습니다.매우 쉽고, 목표-C 2.0 블록을 사용하여 계층의 모든 보기에서 코드를 실행합니다.

https://github.com/egold/UIViewRecursion

예:

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

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

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

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

답변에 게시된 코드는 모든 창, 모든 보기 및 해당 하위 보기를 통과합니다.이것은 NSLog에 보기 계층의 인쇄물을 덤프하는 데 사용되었지만 보기 계층의 모든 이동에 대한 기준으로 사용할 수 있습니다.재귀 C 함수를 사용하여 뷰 트리를 통과합니다.

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

IIRC, 게시된 코드가 작동한 코드입니다.그렇지 않으면 올바른 방향으로 안내해 줄 것입니다.자기 부담으로 사용 등

계층 수준도 표시됩니다.

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

페이지를 먼저 찾았으면 좋았을 텐데, 만약 (어떤 이유로) 카테고리가 아닌 더 많은 코드 행으로 이 작업을 반복적으로 수행하려면

재귀(디버거 옵션 제외)를 사용하는 모든 답변은 카테고리를 사용한 것 같습니다.범주가 필요하지 않거나 원하는 경우 인스턴스 메소드를 사용하면 됩니다.예를 들어 뷰 계층에서 모든 레이블의 배열을 가져와야 하는 경우 이 작업을 수행할 수 있습니다.

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

아래 방법은 하나 이상의 가변 배열을 만든 다음 입력 보기의 하위 보기를 순환합니다.이렇게 하면 초기 하위 뷰를 추가한 다음 해당 하위 뷰에 대한 하위 뷰가 있는지 여부를 쿼리합니다.만약 사실이라면, 그것은 스스로를 다시 부릅니다.계층의 모든 보기가 추가될 때까지 이 작업을 수행합니다.

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

언급URL : https://stackoverflow.com/questions/2746478/how-can-i-loop-through-all-subviews-of-a-uiview-and-their-subviews-and-their-su

반응형