Как перечислить все подвиды в uiviewcontroller в iOS?
Я хочу перечислить все подвиды в UIViewController
. Я пытался!--1-->, но не все подвиды перечислены, например, подвиды в UITableViewCell
не нашли. Есть идеи?
22 ответов
вы должны рекурсивно перебирать представления sub.
- (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()
Если все, что вам нужно, это массив UIView
s, это одно линейное решение (Swift 4+):
extension UIView {
var allSubviews: [UIView] {
return self.subviews.reduce([UIView]()) { + [] + .allSubviews }
}
}
причина, по которой подвиды в UITableViewCell не печатаются, заключается в том, что вы должны выводить все подвиды на верхнем уровне. Подвиды ячейки не являются прямыми подвидами вашего представления.
чтобы получить подвиды UITableViewCell, вам нужно определить, какие подвиды принадлежат UITableViewCell (используя isKindOfClass:
) в вашем цикле печати, а затем цикл через его subviews
Edit: это сообщение в блоге на Легкая Отладка 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()
напрямую, используйте последовательность.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 является тип вашего
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>>
как вы должно быть, догадался, что мой superview имеет 4 подвида:
- a stackView (сам stackView имеет изображение и другой stackView (этот stackView имеет 2 таможни ярлыки))
- 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
}
кратчайшего решения
for subview in self.view.subviews {
print(subview.dynamicType)
}
результат
UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide
Примечания
- как вы можете видеть, этот метод не перечисляет подвиды рекурсивно. См. некоторые другие ответы на этот вопрос.
версия 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.вид.подвиды поддерживают наследственность взглядов.Чтобы получить подвиды uitableviewcell, вам нужно сделать что-то вроде ниже.
for (UIView *subView in self.view.subviews) {
if ([subView isKindOfClass:[UITableView class]]) {
for (UIView *tableSubview in subView.subviews) {
.......
}
}
}