22 ответов:
вы должны рекурсивно перебирать вложенные представления.
- (void)listSubviewsOfView:(UIView *)view { // Get the subviews of the view NSArray *subviews = [view subviews]; // Return if there are no subviews if ([subviews count] == 0) return; // COUNT CHECK LINE for (UIView *subview in subviews) { // Do what you want to do with the subview NSLog(@"%@", subview); // List the subviews of subview [self listSubviewsOfView:subview]; } }
Как прокомментировал @Greg Meletic, вы можете пропустить строку проверки количества выше.
встроенный способ Xcode/gdb для сброса иерархии представлений полезен -- recursiveDescription, per http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
Он выводит более полную иерархию представлений, которую вы можете найти полезной:
> po [_myToolbar recursiveDescription] <UIToolbarButton: 0xd866040; frame = (152 0; 15 44); opaque = NO; layer = <CALayer: 0xd864230>> | <UISwappableImageView: 0xd8660f0; frame = (0 0; 0 0); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xd86a160>>
вы должны печатать рекурсивно, этот метод также вкладки на основе глубины представления
-(void) printAllChildrenOfView:(UIView*) node depth:(int) d { //Tabs are just for formatting NSString *tabs = @""; for (int i = 0; i < d; i++) { tabs = [tabs stringByAppendingFormat:@"\t"]; } NSLog(@"%@%@", tabs, node); d++; //Increment the depth for (UIView *child in node.subviews) { [self printAllChildrenOfView:child depth:d]; } }
вот версия swift
func listSubviewsOfView(view:UIView){ // Get the subviews of the view var subviews = view.subviews // Return if there are no subviews if subviews.count == 0 { return } for subview : AnyObject in subviews{ // Do what you want to do with the subview println(subview) // List the subviews of subview listSubviewsOfView(subview as UIView) } }
Я немного опоздала на вечеринку, но немного более общее решение:
@implementation UIView (childViews) - (NSArray*) allSubviews { __block NSArray* allSubviews = [NSArray arrayWithObject:self]; [self.subviews enumerateObjectsUsingBlock:^( UIView* view, NSUInteger idx, BOOL*stop) { allSubviews = [allSubviews arrayByAddingObjectsFromArray:[view allSubviews]]; }]; return allSubviews; } @end
элегантное рекурсивное решение в Swift:
extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { .subviewsRecursive() } } }вы можете вызвать subviewsRecursive () на любом UIView:
let allSubviews = self.view.subviewsRecursive()
Если все, что вы хотите-это массив
UIViews, это одно линейное решение (Swift 4+):extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { + [] + .allSubviews } } }
причина, по которой подвиды в UITableViewCell не печатаются, заключается в том, что вы должны выводить все подвиды на верхнем уровне. Подвиды ячейки не являются прямыми подвидами вашего представления.
для того, чтобы сделать подпанели в инструменту UITableViewCell, вы должны определить что подпанелей относятся к инструменту UITableViewCell (используя
isKindOfClass:) в вашем цикле печати, а затем цикл через его subviewsредактировать: это сообщение в блоге на Простая Отладка UIView может потенциально помогите
Я написал категорию, чтобы перечислить все представления, удерживаемые контроллером представления, который вдохновлен ответами, опубликованными ранее.
@interface UIView (ListSubviewHierarchy) - (NSString *)listOfSubviews; @end @implementation UIView (ListSubviewHierarchy) - (NSInteger)depth { NSInteger depth = 0; if ([self superview]) { deepth = [[self superview] depth] + 1; } return depth; } - (NSString *)listOfSubviews { NSString * indent = @""; NSInteger depth = [self depth]; for (int counter = 0; counter < depth; counter ++) { indent = [indent stringByAppendingString:@" "]; } __block NSString * listOfSubviews = [NSString stringWithFormat:@"\n%@%@", indent, [self description]; if ([self.subviews count] > 0) { [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { UIView * subview = obj; listOfSubviews = [listOfSubviews stringByAppendingFormat:@"%@", [subview listOfSubviews]]; }]; } return listOfSubviews; } @endчтобы перечислить все представления, удерживаемые контроллером вида, просто
NSLog("%@",[self listOfSubviews]), которыйselfозначает сам контроллер вида. Хотя это не совсем эффективно.плюс, вы можете использовать
NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);сделать то же самое, и я думаю, что это более эффективно, чем моя реализация.
простой пример Swift:
var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) }
на мой взгляд, категория или расширение UIView намного лучше, чем другие и рекурсивный является ключевым моментом, чтобы получить все подвиды
подробнее:
https://github.com/ZhipingYang/XYDebugView
С
@implementation UIView (Recurrence) - (NSArray<UIView *> *)recurrenceAllSubviews { NSMutableArray <UIView *> *all = @[].mutableCopy; void (^getSubViewsBlock)(UIView *current) = ^(UIView *current){ [all addObject:current]; for (UIView *sub in current.subviews) { [all addObjectsFromArray:[sub recurrenceAllSubviews]]; } }; getSubViewsBlock(self); return [NSArray arrayWithArray:all]; } @endпример
NSArray *views = [viewController.view recurrenceAllSubviews];Swift 3.1
extension UIView { func recurrenceAllSubviews() -> [UIView] { var all = [UIView]() func getSubview(view: UIView) { all.append(view) guard view.subviews.count>0 else { return } view.subviews.forEach{ getSubview(view: ) } } getSubview(view: self) return all } }пример
let views = viewController.view.recurrenceAllSubviews()напрямую, используйте последовательность функция для получения все подвиды
let viewSequence = sequence(state: [viewController.view]) { (state: inout [UIView] ) -> [UIView]? in guard state.count > 0 else { return nil } defer { state = state.map{ .subviews }.flatMap{ } } return state } let views = Array(viewSequence).flatMap{ }
подробности
xCode 9.0.1, Swift 4
решение
extension UIView { private var viewInfo: String { return "\(classForCoder), frame: \(frame))" } private func subviews(parentView: UIView, level: Int = 0, printSubviews: Bool = false) -> [UIView] { var result = [UIView]() if level == 0 && printSubviews { result.append(parentView) print("\(parentView.viewInfo)") } for subview in parentView.subviews { if printSubviews { print("\(String(repeating: "-", count: level))\(subview.viewInfo)") } result.append(subview) if subview.subviews.count != 0 { result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews) } } return result } var allSubviews: [UIView] { return subviews(parentView: self) } func printSubviews() { _ = subviews(parentView: self, printSubviews: true) } }использование
view.printSubviews() print("\(view.allSubviews.count)")результат
вы можете попробовать причудливый трюк с массивом, например:
[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];только одна строка кода. Конечно, вам может потребоваться настроить свой метод
printAllChildrenOfViewчтобы не принимать никаких параметров или сделать новый метод.
Swift 2.0 совместимый
здесь рекурсивный метод для получения всех подвидов общего вида:
extension UIView { func subviewsList() -> [UIView] { var subviews = self.subviews if subviews.count == 0 { return subviews + [] } for v in subviews { subviews += v.listSubviewsOfView() } return subviews } }таким образом, вы можете позвонить везде таким образом:
let view = FooController.view let subviews = view.subviewsList()
это переписывание этой ответ:
сначала вы должны получить указатель / ссылку на объект, который вы собираетесь распечатать все его подвиды. Иногда вам может быть проще найти этот объект, обратившись к нему через его подвиды. Как
po someSubview.superview. Это даст вам что-то вроде:Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
- FaceBookApp это код название
- WhatsNewView-это тип вашего
superview0x7f91747c71f0- указатель на супервизор.для печати супервизора необходимо использовать точки останова.
теперь, чтобы сделать этот шаг, вы можете просто нажать на "просмотр иерархии отладки". Нет необходимости в точках останова
потом вы могли бы легко сделать:
po [0x7f91747c71f0 recursiveDescription]который для меня вернулся что-то вроде:
<FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>> | <UIStackView: 0x7f91747c75f0; frame = (45 60; 264 93); layer = <CATransformLayer: 0x610000230ec0>> | | <UIImageView: 0x7f916ef38c30; frame = (10.6667 0; 243 58); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x61000003b840>> | | <UIStackView: 0x7f91747c8230; frame = (44.6667 58; 174.667 35); layer = <CATransformLayer: 0x6100006278c0>> | | | <FacebookApp.CopyableUILabel: 0x7f91747a80b0; baseClass = UILabel; frame = (44 0; 86.6667 16); text = 'What's New'; gestureRecognizers = <NSArray: 0x610000c4a770>; layer = <_UILabelLayer: 0x610000085550>> | | | <FacebookApp.CopyableUILabel: 0x7f916ef396a0; baseClass = UILabel; frame = (0 21; 174.667 14); text = 'Version 14.0.5c Oct 05, 2...'; gestureRecognizers = <NSArray: 0x610000c498a0>; layer = <_UILabelLayer: 0x610000087300>> | <UITextView: 0x7f917015ce00; frame = (45 183; 264 403); text = ' • new Adding new feature...'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x6100000538f0>; layer = <CALayer: 0x61000042f000>; contentOffset: {0, 0}; contentSize: {264, 890}> | | <<_UITextContainerView: 0x7f9170a13350; frame = (0 0; 264 890); layer = <_UITextTiledLayer: 0x6080002c0930>> minSize = {0, 0}, maxSize = {1.7976931348623157e+308, 1.7976931348623157e+308}, textContainer = <NSTextContainer: 0x610000117b20 size = (264.000000,340282346638528859811704183484516925440.000000); widthTracksTextView = YES; heightTracksTextView = NO>; exclusionPaths = 0x61000001bc30; lineBreakMode = 0> | | | <_UITileLayer: 0x60800023f8a0> (layer) | | | <_UITileLayer: 0x60800023f3c0> (layer) | | | <_UITileLayer: 0x60800023f360> (layer) | | | <_UITileLayer: 0x60800023eca0> (layer) | | <UIImageView: 0x7f9170a7d370; frame = (-39 397.667; 36 2.33333); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f4c0>> | | <UIImageView: 0x7f9170a7d560; frame = (258.667 -39; 2.33333 36); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x60800023f5e0>> | <UIView: 0x7f916ef149c0; frame = (0 587; 354 0); layer = <CALayer: 0x6100006392a0>> | <UIButton: 0x7f91747a8730; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x610000639320>> | | <UIButtonLabel: 0x7f916ef00a80; frame = (0 -5.66667; 0 16); text = 'See More Details'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x610000084d80>>как вы должно быть, догадался, что мой супервизор имеет 4 подвида:
- stackView (сам stackView имеет изображение и другой stackView(этот stackView имеет 2 custom ярлыки))
- textView
- вид
наэто довольно ново для меня, но помогло мне отладить фреймы моих представлений (а также текст и тип). Один из моих подвидов не показывался на экране, поэтому использовал recursiveDescription, и я понял, что ширина моего одного из моих подвидов была
0... поэтому я исправил его ограничения, и появилось подвидо.
альтернативно, если вы хотите вернуть массив всех подвидов (и вложенных подвидов) из расширения UIView:
func getAllSubviewsRecursively() -> [AnyObject] { var allSubviews: [AnyObject] = [] for subview in self.subviews { if let subview = subview as? UIView { allSubviews.append(subview) allSubviews = allSubviews + subview.getAllSubviewsRecursively() } } return allSubviews }
версия C# Xamarin:
void ListSubviewsOfView(UIView view) { var subviews = view.Subviews; if (subviews.Length == 0) return; foreach (var subView in subviews) { Console.WriteLine("Subview of type {0}", subView.GetType()); ListSubviewsOfView(subView); } }кроме того, если вы хотите найти все подвиды определенного типа, которые я использую:
List<T> FindViews<T>(UIView view) { List<T> allSubviews = new List<T>(); var subviews = view.Subviews.Where(x => x.GetType() == typeof(T)).ToList(); if (subviews.Count == 0) return allSubviews; foreach (var subView in subviews) { allSubviews.AddRange(FindViews<T>(subView)); } return allSubviews; }
Я сделал это в категории
UIViewпросто вызовите функцию, передающую индекс, чтобы распечатать их с хорошим форматом дерева. Это просто еще один вариант ответа, опубликованного Джеймс Уэбстер.#pragma mark - Views Tree - (void)printSubviewsTreeWithIndex:(NSInteger)index { if (!self) { return; } NSString *tabSpace = @""; @autoreleasepool { for (NSInteger x = 0; x < index; x++) { tabSpace = [tabSpace stringByAppendingString:@"\t"]; } } NSLog(@"%@%@", tabSpace, self); if (!self.subviews) { return; } @autoreleasepool { for (UIView *subView in self.subviews) { [subView printViewsTreeWithIndex:index++]; } } }надеюсь, это поможет :)
- (NSString *)recusiveDescription:(UIView *)view { NSString *s = @""; NSArray *subviews = [view subviews]; if ([subviews count] == 0) return @"no subviews"; for (UIView *subView in subviews) { s = [NSString stringWithFormat:@"<%@; frame = (%f %f : %f %f) \n ",NSStringFromClass([subView class]), subView.frame.origin.x, subView.frame.origin.y ,subView.frame.size.width, subView.frame.size.height]; [self recusiveDescription:subView]; } return s; }
self.вид.подвиды поддерживают наследственность views.To получить подвиды uitableviewcell вы должны сделать что-то вроде ниже.
for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }



Comments