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ł?
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ł?
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.
recursiveDescription
odpowiedź od @natbro - stackoverflow.com/a/8962824/429521
Wbudowany 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>>
[_myToolbar recursiveDescription]
w moim kodzie lub w innym miejscu.
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];
}
}
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)
}
}
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
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 }
}
}
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)")
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
@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];
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 }
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ądy
Edycja: ten post na blogu dotyczący łatwego debugowania UIView może potencjalnie pomóc
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])
, co self
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.
Prosty przykład języka Swift:
var arrOfSub = self.view.subviews
print("Number of Subviews: \(arrOfSub.count)")
for item in arrOfSub {
print(item)
}
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.
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()
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 .
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
}
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;
}
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 :)
- (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;
}