如何循环浏览UIView及其子视图及其子视图的所有子视图?
答案 0 :(得分:120)
使用递归:
// 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];
答案 1 :(得分:32)
这里是我的解决方案,使用递归和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;
}
}
答案 2 :(得分:14)
Swift 3中的一个解决方案,它提供了所有subviews
而不包含视图本身:
extension UIView {
var allSubViews : [UIView] {
var array = [self.subviews].flatMap {$0}
array.forEach { array.append(contentsOf: $0.allSubViews) }
return array
}
}
答案 3 :(得分:12)
通过调试器找到了一种有趣的方法:
http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/
引用此Apple Technote:
https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT
只需确保您的调试器已暂停(或者手动设置暂停的断点),然后您可以要求recursiveDescription
。
答案 4 :(得分:11)
我在创建时标记所有内容。然后很容易找到任何子视图。
view = [aView viewWithTag:tag];
答案 5 :(得分: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];
答案 6 :(得分:5)
这是另一个Swift实现:
extension UIView {
var allSubviews: [UIView] {
return self.subviews.flatMap { [$0] + $0.allSubviews }
}
}
答案 7 :(得分:5)
以下是实际视图循环和中断功能的示例。
<强>夫特:强>
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;
}
}];
答案 8 :(得分: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];
}
}
答案 9 :(得分:4)
无需创建任何新功能。只需在使用Xcode进行调试时就可以了。
在视图控制器中设置断点,并使应用程序在此断点处暂停。
右键单击空白区域,然后在Xcode的Watch窗口中按“Add Expression ...”。
输入以下行:
(NSString*)[self->_view recursiveDescription]
如果值太长,请右键单击它并选择“打印描述...”。您将在控制台窗口中看到self.view的所有子视图。如果您不想查看self.view的子视图,请将self-&gt; _view更改为其他内容。
完成!没有gdb!
答案 10 :(得分:3)
顺便说一句,我做了一个开源项目来帮助完成这类任务。它非常简单,并使用Objective-C 2.0块来在层次结构中的所有视图上执行代码。
https://github.com/egold/UIViewRecursion
示例:
-(void)makeAllSubviewsGreen
{
[self.view runBlockOnAllSubviews:^(UIView *view) {
view.backgroundColor = [UIColor greenColor];
}];
}
答案 11 :(得分:2)
以上是Ole Begemann's answer的变体,其中添加了缩进以说明层次结构:
// 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
答案 12 :(得分:1)
this answer中发布的代码遍历所有窗口以及所有视图及其所有子视图。它用于将视图层次结构的打印输出转储到NSLog,但您可以将其用作任何遍历视图层次结构的基础。它使用递归C函数遍历视图树。
答案 13 :(得分:0)
希望我首先找到this page,但是如果(出于某种原因)你想要非递归地执行此操作,而不是在类别中,并且需要更多行代码
答案 14 :(得分:0)
这也显示层次结构级别
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(int)level
{
NSLog(@"%d - %@", level, self);
for (UIView *subview in self.subviews)
{
[subview logViewHierarchy:(level+1)];
}
}
@end
答案 15 :(得分: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
}
}
答案 16 :(得分:0)
我写了a category一段时间来调试一些观点。
IIRC,发布的代码是有效的。如果没有,它会指向正确的方向。使用风险等等。
答案 17 :(得分:-1)
下面的方法创建一个或多个可变数组,然后遍历输入视图的子视图。在这样做时,它会添加初始子视图,然后查询是否存在该子视图的任何子视图。如果是真的,它会再次调用自己。它会这样做,直到添加了层次结构的所有视图。
-(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];