UIView のすべてのサブビュー、およびそのサブビューとそのサブビューをループするにはどうすればよいですか?
18 に答える
再帰を使用します。
// 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;
}
}
作成時にすべてにタグを付けます。そうすれば、サブビューを簡単に見つけることができます。
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+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];
再帰コードは次のとおりです。-
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];
}
}
新しい関数を作成する必要はありません。Xcodeでデバッグするときにそれを行うだけです。
ビュー コントローラーにブレークポイントを設定し、このブレークポイントでアプリを一時停止します。
空の領域を右クリックし、Xcode のウォッチ ウィンドウで [式の追加...] を押します。
次の行を入力します。
(NSString*)[self->_view recursiveDescription]
値が長すぎる場合は、右クリックして [... の説明を印刷] を選択します。コンソール ウィンドウに self.view のすべてのサブビューが表示されます。self.view のサブビューを表示したくない場合は、self->_view を別のものに変更します。
終わり!いいえgdb!
ところで、私はこの種のタスクを支援するためにオープン ソース プロジェクトを作成しました。とても簡単で、Objective-C 2.0 ブロックを使用して、階層内のすべてのビューでコードを実行します。
https://github.com/egold/UIViewRecursion
例:
-(void)makeAllSubviewsGreen
{
[self.view runBlockOnAllSubviews:^(UIView *view) {
view.backgroundColor = [UIColor greenColor];
}];
}
これは、上記の 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
この回答に投稿されたコードは、すべてのウィンドウ、すべてのビュー、およびそれらのすべてのサブビューをトラバースします。ビュー階層の出力を NSLog にダンプするために使用されましたが、ビュー階層のトラバーサルの基礎として使用できます。再帰的な C 関数を使用して、ビュー ツリーをトラバースします。
再帰を使用したすべての回答(デバッガーオプションを除く)はカテゴリを使用したと思います。カテゴリが不要な場合は、インスタンス メソッドを使用できます。たとえば、ビュー階層内のすべてのラベルの配列を取得する必要がある場合、これを行うことができます。
@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
}
}
いくつかのビューをデバッグするために、しばらく前にカテゴリを書きました。
IIRC、投稿されたコードは機能したものです。そうでない場合は、正しい方向に向けます。自己責任等でご利用ください。
これにより、階層レベルも表示されます
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(int)level
{
NSLog(@"%d - %@", level, self);
for (UIView *subview in self.subviews)
{
[subview logViewHierarchy:(level+1)];
}
}
@end
このページを最初に見つけたかったのですが、(何らかの理由で)これを非再帰的に、カテゴリではなく、より多くのコード行で行いたい場合