Я хочу перечислить все подвиды в UIViewController
. Я пробовал self.view.subviews
, но не все вложенные представления перечислены, например, вложенные UITableViewCell
представления в не найдены. Любая идея?
ios
uiview
subview
uiview-hierarchy
user403015
источник
источник
Ответы:
Вы должны рекурсивно перебирать подвиды.
- (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, вы можете пропустить СЧЕТЧИК ПРОВЕРКИ выше.
источник
recursiveDescription
ответ от @natbro - stackoverflow.com/a/8962824/429521Полезен встроенный в xcode / gdb способ дампа иерархии представлений - recursiveDescription, согласно 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>>
источник
[_myToolbar recursiveDescription]
в моем коде или где-нибудь еще.Элегантное рекурсивное решение в Swift:
extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { $0.subviewsRecursive() } } }
Вы можете вызвать subviewsRecursive () в любом UIView:
let allSubviews = self.view.subviewsRecursive()
источник
Вам нужно печатать рекурсивно, этот метод также включает вкладки в зависимости от глубины просмотра
-(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]; } }
источник
Вот быстрая версия
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
источник
Если все, что вам нужно, это массив
UIView
s, это однострочное решение ( Swift 4+ ):extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews } } }
источник
Я использую такой способ:
NSLog(@"%@", [self.view subviews]);
в UIViewController.
источник
Детали
Решение
extension UIView { 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.isEmpty { continue } result += subviews(parentView: subview, level: level+1, printSubviews: printSubviews) } return result } private var viewInfo: String { return "\(classForCoder), frame: \(frame))" } var allSubviews: [UIView] { return subviews(parentView: self) } func printSubviews() { _ = subviews(parentView: self, printSubviews: true) } }
Применение
view.printSubviews() print("\(view.allSubviews.count)")
Результат
источник
На мой взгляд, категория или расширение UIView намного лучше, чем другие, и рекурсивный является ключевым моментом для получения всех подвидов.
учить больше:
https://github.com/ZhipingYang/XYDebugView
Цель-C
@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: $0) } } 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{ $0.subviews }.flatMap{ $0 } } return state } let views = viewSequence.flatMap{ $0 }
источник
Причина, по которой подпредставления в UITableViewCell не печатаются, заключается в том, что вы должны выводить все подвиды на верхнем уровне. Подпредставления ячейки не являются прямыми подпредставлениями вашего представления.
Чтобы получить подпредставления UITableViewCell, вам необходимо определить, какие подпредставления принадлежат UITableViewCell (используя
isKindOfClass:
) в вашем цикле печати, а затем перебрать их в цикле.Изменить: это сообщение в блоге о простой отладке 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) }
источник
Вы можете попробовать необычный трюк с массивом, например:
[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()
источник
Кратчайшее решение
for subview in self.view.subviews { print(subview.dynamicType) }
Результат
UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide
Примечания
источник
Это переписывание этого ответа:
Сначала вы должны получить указатель / ссылку на объект, который вы собираетесь распечатать, все его подвиды. Иногда вам может быть проще найти этот объект, открыв его через его подпредставление. Нравится
po someSubview.superview
. Это даст вам что-то вроде:Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
superview
0x7f91747c71f0
указатель на супервизор.Чтобы распечатать superView, вы должны использовать точки останова.
Теперь, чтобы сделать этот шаг, вы можете просто щелкнуть «просмотреть иерархию отладки». Нет необходимости в точках останова
Тогда вы могли бы легко сделать:
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 подпредставления:
Это довольно ново для меня, но помогло мне отладить фреймы моих представлений (а также текст и тип). Одно из моих подпредставлений не отображалось на экране, поэтому использовал рекурсивное описание, и я понял, что ширина одного из моих подпредставлений была
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 }
источник
Версия AC # 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.view.subviews поддерживает иерархию представлений. Чтобы получить подпредставления uitableviewcell, вы должны сделать что-то, как показано ниже.
for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }
источник