Chcę wymienić wszystkie podglądy w pliku UIViewController
. Próbowałem self.view.subviews
, ale nie wszystkie podglądy są wymienione, na przykład podglądy podrzędne w UITableViewCell
nie zostały znalezione. Dowolny pomysł?
ios
uiview
subview
uiview-hierarchy
user403015
źródło
źródło
Odpowiedzi:
Musisz rekurencyjnie iterować widoki podrzędne.
- (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]; } }
Jak skomentował @Greg Meletic, możesz pominąć LICZNIK LINII SPRAWDZANIA powyżej.
źródło
recursiveDescription
odpowiedź od @natbro - stackoverflow.com/a/8962824/429521Wbudowany sposób xcode / gdb do zrzucania hierarchii widoków jest przydatny - recursiveDescription, na http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
Wyświetla bardziej kompletną hierarchię widoków, która może okazać się przydatna:
> 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>>
źródło
[_myToolbar recursiveDescription]
w moim kodzie lub w innym miejscu.Eleganckie rozwiązanie rekurencyjne w Swift:
extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { $0.subviewsRecursive() } } }
Możesz wywołać subviewsRecursive () w dowolnym UIView:
let allSubviews = self.view.subviewsRecursive()
źródło
Musisz drukować rekurencyjnie, ta metoda również wyświetla karty w oparciu o głębokość widoku
-(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]; } }
źródło
Oto szybka wersja
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) } }
źródło
Trochę spóźniłem się na imprezę, ale rozwiązanie nieco bardziej ogólne:
@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
źródło
Jeśli wszystko, czego potrzebujesz, to tablica
UIView
s, jest to rozwiązanie typu one liner ( Swift 4+ ):extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews } } }
źródło
Używam w ten sposób:
NSLog(@"%@", [self.view subviews]);
w UIViewController.
źródło
Detale
Rozwiązanie
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) } }
Stosowanie
view.printSubviews() print("\(view.allSubviews.count)")
Wynik
źródło
Na swój sposób kategoria lub rozszerzenie UIView jest znacznie lepsze niż inne, a rekurencja jest kluczowym punktem, aby uzyskać wszystkie podglądy
Ucz się więcej:
https://github.com/ZhipingYang/XYDebugView
Cel 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
przykład
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 } }
przykład
let views = viewController.view.recurrenceAllSubviews()
bezpośrednio, użyj funkcji sekwencji, aby uzyskać wszystkie podglądy
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 }
źródło
Przyczyną, dla której podglądy w UITableViewCell nie są drukowane, jest to, że musisz wypisywać wszystkie podglądy na najwyższym poziomie. Podglądy komórki nie są bezpośrednimi widokami podrzędnymi Twojego widoku.
Aby uzyskać widoki podrzędne UITableViewCell, musisz określić, które podwidoki należą do UITableViewCell (używając
isKindOfClass:
) w pętli drukowania, a następnie przejrzeć jego podglądyEdycja: ten post na blogu dotyczący łatwego debugowania UIView może potencjalnie pomóc
źródło
Napisałem kategorię, aby wymienić wszystkie widoki posiadane przez kontroler widoku, zainspirowane odpowiedziami zamieszczonymi wcześniej.
@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
Aby wyświetlić wszystkie widoki posiadanych przez kontrolera widoku, po prostu
NSLog("%@",[self listOfSubviews])
, coself
oznacza, że widok kontrolera siebie. Chociaż nie jest to dość wydajne.Dodatkowo możesz użyć
NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);
do zrobienia tego samego i myślę, że jest to bardziej wydajne niż moja implementacja.źródło
Prosty przykład języka Swift:
var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) }
źródło
Możesz wypróbować fantazyjną sztuczkę tablicową, na przykład:
[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];
Tylko jedna linia kodu. Oczywiście może być konieczne dostosowanie metody,
printAllChildrenOfView
aby nie przyjmowała żadnych parametrów ani nie tworzyła nowej metody.źródło
Kompatybilny ze Swift 2.0
Tutaj rekurencyjna metoda uzyskiwania wszystkich podwidoków widoku ogólnego:
extension UIView { func subviewsList() -> [UIView] { var subviews = self.subviews if subviews.count == 0 { return subviews + [] } for v in subviews { subviews += v.listSubviewsOfView() } return subviews } }
Możesz więc dzwonić wszędzie w ten sposób:
let view = FooController.view let subviews = view.subviewsList()
źródło
Najkrótsze rozwiązanie
for subview in self.view.subviews { print(subview.dynamicType) }
Wynik
UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide
Uwagi
źródło
To przepisanie tej odpowiedzi:
Najpierw musisz pobrać wskaźnik / odniesienie do obiektu, który chcesz wydrukować, wszystkie jego podglądy. Czasami łatwiej jest znaleźć ten obiekt, uzyskując dostęp do niego za pośrednictwem jego podglądu. Lubię
po someSubview.superview
. To da ci coś takiego:Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
superview
0x7f91747c71f0
jest wskaźnikiem do superviewu.Aby wydrukować superView, musisz użyć punktów przerwania.
Teraz, aby wykonać ten krok, wystarczy kliknąć opcję „Wyświetl hierarchię debugowania”. Nie ma potrzeby stosowania punktów przerwania
Wtedy możesz łatwo zrobić:
po [0x7f91747c71f0 recursiveDescription]
który dla mnie zwrócił coś takiego:
<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>>
jak pewnie zgadłeś, mój nadzór ma 4 podglądy:
Jest to dla mnie całkiem nowe, ale pomogło mi debugować ramki moich widoków (oraz tekst i typ). Jeden z moich podglądów podrzędnych nie pojawił się na ekranie, więc użyłem recursiveDescription i zdałem sobie sprawę, że szerokość mojego jednego z moich podglądów podrzędnych była
0
... więc poszedłem poprawić jego ograniczenia i pojawił się podwidok .źródło
Alternatywnie, jeśli chcesz zwrócić tablicę wszystkich widoków podrzędnych (i zagnieżdżonych widoków podrzędnych) z rozszerzenia 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 }
źródło
Wersja 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); } }
Alternatywnie, jeśli chcesz znaleźć wszystkie podglądy określonego typu, których używam:
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; }
źródło
Zrobiłem to w kategorii, w której
UIView
wystarczy wywołać funkcję przekazującą indeks, aby wydrukować je w ładnym formacie drzewa. To tylko kolejna opcja odpowiedzi zamieszczonej przez Jamesa Webstera .#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++]; } } }
Mam nadzieję, że to pomoże :)
źródło
- (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; }
źródło
self.view.subviews zachowuje heirarchię widoków. Aby uzyskać subviews uitableviewcell, musisz zrobić coś jak poniżej.
for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }
źródło