web-dev-qa-db-ja.com

UIViewのすべてのサブビュー、およびそれらのサブビューとサブビューをループする方法はありますか

UIViewのすべてのサブビュー、およびそのサブビューとサブビューをループするにはどうすればよいですか?

76
123hal321

再帰を使用する:

// 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];
120
Ole Begemann

さて、ここに再帰と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;
     }
}
33

ビュー自体を含めずにすべてのsubviewsを提供するSwift 3のソリューション:

extension UIView {
var allSubViews : [UIView] {

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

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

        return array
    }
}
14
ielyamani

デバッガーでこれを行う興味深い方法を見つけました。

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

これを参照Apple Technote:

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

デバッガーが一時停止されていることを確認し(手動で一時停止のブレークポイントを設定する)、recursiveDescriptionを要求できます。

12
djibouti33

作成時にすべてにタグを付けます。その後、サブビューを簡単に見つけることができます。

view = [aView viewWithTag:tag];

オレ・ベゲマンの助けを借りて。ブロックの概念を組み込むために数行追加しました。

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

別のSwift実装:

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

次に、実際のビューのループ機能と破壊機能の例を示します。

スイフト:

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;
    }
}];
5
Vahram Dodoryan

新しい関数を作成する必要はありません。 Xcodeでデバッグするときに実行してください。

View Controllerにブレークポイントを設定し、このブレークポイントでアプリを一時停止します。

空の領域を右クリックし、Xcodeの[ウォッチ]ウィンドウで[式を追加...]を押します。

次の行を入力します。

(NSString*)[self->_view recursiveDescription]

値が長すぎる場合は、右クリックして「...の説明を印刷」を選択します。コンソールウィンドウにself.viewのすべてのサブビューが表示されます。 self.viewのサブビューを表示したくない場合は、self-> _ viewを別のものに変更します。

できた! gdbはありません!

4
Vince Yuan

再帰的なコードは次のとおりです。

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

ところで、私はこの種のタスクを支援するオープンソースプロジェクトを作成しました。とても簡単で、Objective-C 2.0ブロックを使用して、階層内のすべてのビューでコードを実行します。

https://github.com/egold/UIViewRecursion

例:

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

        view.backgroundColor = [UIColor greenColor];
    }];
}
3
Eric Goldberg

ここに 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
2
jaredsinclair

この回答 に投稿されたコードは、すべてのウィンドウとすべてのビュー、およびそれらのすべてのサブビューを走査します。ビュー階層のプリントアウトをNSLogにダンプするために使用されましたが、ビュー階層のあらゆるトラバースのベースとして使用できます。再帰的なC関数を使用して、ビューツリーを走査します。

1
progrmr

カテゴリ いくつかのビューをデバッグするためにしばらく前に書きました。

IIRC、投稿されたコードが機能しました。そうでない場合、それはあなたを正しい方向に向けます。自己責任で使用するなど.

0
TechZen

このページ が最初に見つかりましたが、(何らかの理由で)カテゴリではなく、より多くのコード行でこれを非再帰的に実行したい場合

0
smlxl

階層レベルも表示されます

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

再帰を使用するすべての回答(デバッガーオプションを除く)はカテゴリを使用したと思います。カテゴリが必要ない場合は、インスタンスメソッドを使用できます。たとえば、ビュー階層内のすべてのラベルの配列を取得する必要がある場合、これを実行できます。

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