Я хочу перечислить все подвиды в UIViewController
. Я пробовал self.view.subviews
, но не все вложенные представления перечислены, например, вложенные UITableViewCell
представления в не найдены. Любая идея?
Я хочу перечислить все подвиды в UIViewController
. Я пробовал self.view.subviews
, но не все вложенные представления перечислены, например, вложенные UITableViewCell
представления в не найдены. Любая идея?
Ответы:
Вы должны рекурсивно перебирать подвиды.
- (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]
в моем коде или где-нибудь еще.
Вам нужно печатать рекурсивно, этот метод также включает вкладки в зависимости от глубины просмотра
-(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
@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];
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()
Это переписывание этого ответа:
Сначала вы должны получить указатель / ссылку на объект, который вы собираетесь распечатать, все его подвиды. Иногда вам может быть проще найти этот объект, открыв его через его подпредставление. Нравится 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) {
.......
}
}
}