Je veux lister toutes les sous-vues dans un fichier UIViewController
. J'ai essayé self.view.subviews
, mais toutes les sous-vues ne sont pas répertoriées, par exemple, les sous-vues de UITableViewCell
ne sont pas trouvées. Une idée?
Je veux lister toutes les sous-vues dans un fichier UIViewController
. J'ai essayé self.view.subviews
, mais toutes les sous-vues ne sont pas répertoriées, par exemple, les sous-vues de UITableViewCell
ne sont pas trouvées. Une idée?
Réponses:
Vous devez itérer récursivement les sous-vues.
- (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];
}
}
Comme l'a commenté @Greg Meletic, vous pouvez ignorer la COUNT CHECK LINE ci-dessus.
recursiveDescription
réponse beaucoup plus simple de @natbro - stackoverflow.com/a/8962824/429521
La méthode intégrée xcode / gdb pour vider la hiérarchie des vues est utile - recursiveDescription, par http://developer.apple.com/library/ios/#technotes/tn2239/_index.html
Il génère une hiérarchie de vues plus complète que vous pourriez trouver utile:
> 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]
dans mon code ou ailleurs.
Vous devez imprimer de manière récursive, cette méthode utilise également des onglets en fonction de la profondeur de la vue
-(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];
}
}
Voici la version rapide
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)
}
}
Je suis un peu en retard à la fête, mais solution un peu plus générale:
@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
Si tout ce que vous voulez est un tableau de UIView
s, c'est une solution à une seule ligne ( 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)")
À ma manière, la catégorie ou l'extension d'UIView est bien meilleure que les autres et récursive est le point clé pour obtenir toutes les sous-vues
apprendre encore plus:
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
exemple
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
}
}
exemple
let views = viewController.view.recurrenceAllSubviews()
directement, utilisez la fonction de séquence pour obtenir toutes les sous-vues
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 }
La raison pour laquelle les sous-vues dans un UITableViewCell ne sont pas imprimées est que vous devez générer toutes les sous-vues du niveau supérieur. Les sous-vues de la cellule ne sont pas les sous-vues directes de votre vue.
Afin d'obtenir les sous-vues de UITableViewCell, vous devez déterminer les sous-vues appartenant à un UITableViewCell (en utilisant isKindOfClass:
) dans votre boucle d'impression, puis parcourez ses sous-vues.
Edit: Ce billet de blog sur le débogage Easy UIView peut potentiellement aider
J'ai écrit une catégorie pour répertorier toutes les vues détenues par un contrôleur de vue inspiré des réponses publiées auparavant.
@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
Pour lister toutes les vues détenues par un contrôleur de vue, juste NSLog("%@",[self listOfSubviews])
, ce qui self
signifie le contrôleur de vue lui-même. Bien que ce ne soit pas efficace.
De plus, vous pouvez utiliser NSLog(@"\n%@", [(id)self.view performSelector:@selector(recursiveDescription)]);
pour faire la même chose, et je pense que c'est plus efficace que ma mise en œuvre.
Exemple simple de Swift:
var arrOfSub = self.view.subviews
print("Number of Subviews: \(arrOfSub.count)")
for item in arrOfSub {
print(item)
}
Vous pouvez essayer une astuce de tableau sophistiquée, comme:
[self.view.subviews makeObjectsPerformSelector: @selector(printAllChildrenOfView)];
Juste une ligne de code. Bien sûr, vous devrez peut-être ajuster votre méthode printAllChildrenOfView
pour ne prendre aucun paramètre ou créer une nouvelle méthode.
Compatible Swift 2.0
Voici une méthode récursive pour obtenir toutes les sous-vues d'une vue générique:
extension UIView {
func subviewsList() -> [UIView] {
var subviews = self.subviews
if subviews.count == 0 {
return subviews + []
}
for v in subviews {
subviews += v.listSubviewsOfView()
}
return subviews
}
}
Ainsi, vous pouvez appeler partout de cette manière:
let view = FooController.view
let subviews = view.subviewsList()
Solution la plus courte
for subview in self.view.subviews {
print(subview.dynamicType)
}
Résultat
UIView
UIView
UISlider
UISwitch
UITextField
_UILayoutGuide
_UILayoutGuide
Remarques
C'est une réécriture de ceci réponse:
Vous devez d'abord obtenir le pointeur / la référence de l'objet que vous souhaitez imprimer toutes ses sous-vues. Parfois, vous pouvez trouver plus facile de trouver cet objet en y accédant via sa sous-vue. Comme po someSubview.superview
. Cela vous donnera quelque chose comme:
Optional<UIView>
▿ some : <FacebookApp.WhatsNewView: 0x7f91747c71f0; frame = (30 50; 354 636); clipsToBounds = YES; layer = <CALayer: 0x6100002370e0>>
superview
0x7f91747c71f0
est le pointeur vers le superview.Pour imprimer le superView, vous devez utiliser des points d'arrêt.
Maintenant, pour faire cette étape, vous pouvez simplement cliquer sur «Afficher la hiérarchie de débogage». Pas besoin de points d'arrêt
Ensuite, vous pouvez facilement faire:
po [0x7f91747c71f0 recursiveDescription]
qui pour moi a renvoyé quelque chose comme:
<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>>
comme vous devez l'avoir deviné, ma supervision a 4 sous-vues:
C'est assez nouveau pour moi, mais cela m'a aidé à déboguer les cadres de mes vues (ainsi que le texte et le type). Une de mes sous-vues n'apparaissait pas à l'écran, alors j'ai utilisé recursiveDescription et j'ai réalisé que la largeur de l'une de mes sous-vues était 0
... alors j'ai corrigé ses contraintes et la sous-vue apparaissait.
Sinon, si vous souhaitez renvoyer un tableau de toutes les sous-vues (et sous-vues imbriquées) à partir d'une extension 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
}
Version 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);
}
}
Alternativement, si vous souhaitez trouver toutes les sous-vues d'un type spécifique que j'utilise:
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;
}
Je l'ai fait dans une catégorie d' UIView
appeler simplement la fonction en passant l'index pour les imprimer avec un joli format d'arbre. Ceci est juste une autre option de la réponse publiée par 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++];
}
}
}
J'espère que cela aide :)
- (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 maintient la hiérarchie des vues. Pour obtenir des sous-vues de uitableviewcell, vous devez faire quelque chose comme ci-dessous.
for (UIView *subView in self.view.subviews) {
if ([subView isKindOfClass:[UITableView class]]) {
for (UIView *tableSubview in subView.subviews) {
.......
}
}
}