Saya ingin membuat daftar semua subview di UIViewController
. Saya sudah mencoba self.view.subviews
, tetapi tidak semua subview dicantumkan, misalnya subview UITableViewCell
tidak ditemukan. Ada ide?
ios
uiview
subview
uiview-hierarchy
pengguna403015
sumber
sumber
Jawaban:
Anda harus mengulang tampilan sub-tampilan secara rekursif.
- (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]; } }
Seperti yang dikomentari oleh @Greg Meletic, Anda dapat melewati COUNT LINE CHECK di atas.
sumber
recursiveDescription
jawaban yang lebih sederhana dari @natbro - stackoverflow.com/a/8962824/429521Cara bawaan xcode / gdb untuk membuang hierarki tampilan berguna - recursiveDescription, sesuai http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
Ini menghasilkan hierarki tampilan yang lebih lengkap yang mungkin berguna bagi Anda:
> 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>>
sumber
[_myToolbar recursiveDescription]
di kode saya atau di tempat lain.Solusi rekursif yang elegan di Swift:
extension UIView { func subviewsRecursive() -> [UIView] { return subviews + subviews.flatMap { $0.subviewsRecursive() } } }
Anda dapat memanggil subviewsRecursive () di UIView mana pun:
let allSubviews = self.view.subviewsRecursive()
sumber
Anda perlu mencetak secara rekursif, metode ini juga tab berdasarkan kedalaman tampilan
-(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]; } }
sumber
Ini adalah versi cepatnya
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) } }
sumber
Saya agak terlambat ke pesta, tetapi solusi yang sedikit lebih umum:
@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
sumber
Jika yang Anda inginkan hanyalah array
UIView
s, ini adalah solusi satu liner ( Swift 4+ ):extension UIView { var allSubviews: [UIView] { return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews } } }
sumber
Saya menggunakan cara ini:
NSLog(@"%@", [self.view subviews]);
di UIViewController.
sumber
Detail
Larutan
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) } }
Pemakaian
view.printSubviews() print("\(view.allSubviews.count)")
Hasil
sumber
Menurut pendapat saya, kategori atau ekstensi UIView jauh lebih baik daripada yang lain dan rekursif adalah poin kunci untuk mendapatkan semua subview.
Belajarlah lagi:
https://github.com/ZhipingYang/XYDebugView
Objective-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
contoh
NSArray *views = [viewController.view recurrenceAllSubviews];
Swift 3.1.0
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 } }
contoh
let views = viewController.view.recurrenceAllSubviews()
secara langsung, gunakan fungsi sequence untuk mendapatkan semua subview
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 }
sumber
Alasan subview di UITableViewCell tidak dicetak adalah karena Anda harus mengeluarkan semua subview di tingkat atas. Tampilan sel bukanlah sub-tampilan langsung dari tampilan Anda.
Untuk mendapatkan subview UITableViewCell, Anda perlu menentukan subview mana yang termasuk dalam UITableViewCell (menggunakan
isKindOfClass:
) di loop cetak Anda dan kemudian mengulang melalui subviewnya.Sunting: Posting blog tentang Easy UIView Debugging ini berpotensi membantu
sumber
Saya menulis kategori untuk mendaftar semua tampilan yang dipegang oleh pengontrol tampilan yang terinspirasi oleh jawaban yang diposting sebelumnya.
@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
Untuk mendaftar semua tampilan yang dipegang oleh pengontrol tampilan, hanya
NSLog("%@",[self listOfSubviews])
, yangself
berarti pengontrol tampilan itu sendiri. Padahal itu tidak berhenti efisien.Selain itu, Anda dapat menggunakan
NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);
untuk melakukan hal yang sama, dan menurut saya ini lebih efisien daripada penerapan saya.sumber
Contoh Swift sederhana:
var arrOfSub = self.view.subviews print("Number of Subviews: \(arrOfSub.count)") for item in arrOfSub { print(item) }
sumber
Anda bisa mencoba trik array yang mewah, seperti:
[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];
Hanya satu baris kode. Tentu saja, Anda mungkin perlu menyesuaikan metode Anda
printAllChildrenOfView
agar tidak mengambil parameter apa pun atau membuat metode baru.sumber
Kompatibel dengan Swift 2.0
Di sini metode rekursif untuk mendapatkan semua subview dari tampilan generik:
extension UIView { func subviewsList() -> [UIView] { var subviews = self.subviews if subviews.count == 0 { return subviews + [] } for v in subviews { subviews += v.listSubviewsOfView() } return subviews } }
Jadi Anda dapat menelepon ke mana saja dengan cara ini:
let view = FooController.view let subviews = view.subviewsList()
sumber
Solusi terpendek
for subview in self.view.subviews { print(subview.dynamicType) }
Hasil
UIView UIView UISlider UISwitch UITextField _UILayoutGuide _UILayoutGuide
Catatan
sumber
Ini adalah penulisan ulang dari ini jawaban :
Anda harus terlebih dahulu mendapatkan penunjuk / referensi ke objek yang ingin Anda cetak semua subviewnya. Terkadang Anda mungkin lebih mudah menemukan objek itu dengan mengaksesnya melalui subviewnya. Suka
po someSubview.superview
. Ini akan memberi Anda sesuatu seperti:Optional<UIView> ▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
superview
0x7f91747c71f0
adalah penunjuk ke tampilan super.Untuk mencetak superView, Anda harus menggunakan breakpoint.
Sekarang untuk melakukan langkah ini, Anda cukup mengklik 'lihat hierarki debug'. Tidak perlu breakpoint
Maka Anda dapat dengan mudah melakukan:
po [0x7f91747c71f0 recursiveDescription]
yang bagi saya mengembalikan sesuatu seperti:
<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>>
seperti yang Anda tebak, superview saya memiliki 4 subview:
Ini cukup baru bagi saya, tetapi telah membantu saya men-debug bingkai tampilan saya (dan teks dan jenis). Salah satu subview saya tidak muncul di layar, jadi gunakan recursiveDescription dan saya menyadari lebar salah satu subview saya adalah
0
... jadi saya mengoreksi batasannya dan subview muncul.sumber
Atau jika Anda ingin mengembalikan array dari semua subview (dan subview bersarang) dari UIView Extension:
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 }
sumber
Versi 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); } }
Atau, jika Anda ingin menemukan semua subview dari tipe tertentu yang saya gunakan:
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; }
sumber
Saya telah melakukannya dalam kategori
UIView
panggil saja fungsi yang melewati indeks untuk mencetaknya dengan format pohon yang bagus. Ini hanyalah opsi lain dari jawaban yang diposting oleh James Webster .#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++]; } } }
Saya harap ini membantu :)
sumber
- (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; }
sumber
self.view.subviews mempertahankan hierarki pandangan.Untuk mendapatkan subviews dari uitableviewcell Anda harus melakukan sesuatu seperti di bawah ini.
for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UITableView class]]) { for (UIView *tableSubview in subView.subviews) { ....... } } }
sumber