web-dev-qa-db-fra.com

Swift: parcourez toutes les sous-vues de manière récursive pour trouver une classe spécifique et l'ajouter à un tableau

Avoir le diable du temps à essayer de comprendre cela. J'ai posé une question similaire ici: Swift: Obtenir toutes les sous-vues d'un type spécifique et les ajouter à un tableau

Pendant que cela fonctionnait, j’ai réalisé qu’il existait de nombreuses sous-vues et sous-vues, et j’ai donc besoin d’une fonction qui commence par la fenêtre principale UIView, parcourt toutes les sous-vues (et leurs sous-vues jusqu’à ce qu’il ne reste plus aucune) et l’ajoute à un tableau pour une classe de boutons personnalisée que j'ai nommée CheckCircle.

Essentiellement, j'aimerais terminer avec un tableau de CheckCircles qui constituent tous les CheckCircles ajoutés à cette vue par programme.

Des idées? Voici ce sur quoi j'ai travaillé. Il ne semble pas ajouter de Checkcircles au tableau:

    func getSubviewsOfView(v:UIView) -> [CheckCircle] {
        var circleArray = [CheckCircle]()
        // Get the subviews of the view

        var subviews = v.subviews

        if subviews.count == 0 {
            return circleArray
        }

        for subview : AnyObject in subviews{
  if let viewToAppend = subview as? CheckCircle {
        circleArray.append(viewToAppend as CheckCircle)
      }
            getSubviewsOfView(subview as! UIView)
        }
        return circleArray
    }
16
Kenji Crosland

Votre principal problème est que lorsque vous appelez getSubviewsOfView(subview as! UIView) (de manière récursive, dans la fonction), vous ne faites rien avec le résultat.

Vous pouvez également supprimer la vérification count == 0 car, dans ce cas, la boucle for…in sera simplement ignorée. Vous avez également un tas de moulages inutiles

En supposant que votre désir soit d'obtenir un tableau plat d'instances CheckCircle, je pense que cette adaptation de votre code devrait fonctionner:

func getSubviewsOfView(v:UIView) -> [CheckCircle] {
    var circleArray = [CheckCircle]()

    for subview in v.subviews as! [UIView] {
        circleArray += getSubviewsOfView(subview)

        if subview is CheckCircle {
            circleArray.append(subview as! CheckCircle)
        }
    }

    return circleArray
}
20
Aaron Brager

Basé sur Aaron Brager et ullstrm answers

Détails

Swift 4, Xcode 9.1

Solution

extension UIView {

    class func getAllSubviews<T: UIView>(view: UIView) -> [T] {
        return view.subviews.flatMap { subView -> [T] in
            var result = getAllSubviews(view: subView) as [T]
            if let view = subView as? T {
                result.append(view)
            }
            return result
        }
    }

    func getAllSubviews<T: UIView>() -> [T] {
        return UIView.getAllSubviews(view: self) as [T]
    }
}

Usage

let allLabels = simpleView.getAllSubviews() as [UILabel]
18
Vasily Bodnarchuk

Mon approche avec Swift 3 et les génériques! 

private func getSubviewsOf<T: UIView>(view: UIView) -> [T] {
    var subviews = [T]()

    for subview in view.subviews {
        subviews += getSubviewsOf(view: subview) as [T]

        if let subview = subview as? T {
            subviews.append(subview)
        }
    }

    return subviews
}

Pour récupérer tous les UILabel dans une hiérarchie de vues, procédez comme suit:

let allLabels: [UILabel] = getSubviewsOf(view: theView)
14
ullstrm

Vous pouvez le mettre en œuvre simplement en étendant UIView et en définissant les fonctions suivantes.

Swift4 Code

extension UIView {
    func findViews<T: UIView>(subclassOf: T.Type) -> [T] {
        return recursiveSubviews.compactMap { $0 as? T }
    }

    var recursiveSubviews: [UIView] {
        return subviews + subviews.flatMap { $0.recursiveSubviews }
    }
}

Usage

findViews(subclassOf: UILabel.self)
findViews(subclassOf: CheckCircle.self)
2
Shota Nakagami

uITextField n'est plus sur la couche supérieure des sous-vues, j'utilise donc cette méthode:

@implementation UISearchBar (changeFont)

- (void)setFont:(UIFont *)font {
    for (UIView *v in [self subviews]) {
        if ([v isKindOfClass:[UITextField class]]) {
            UITextField *tf = (UITextField *)v;
            tf.font = font;
            UILabel *l = (UILabel *)[tf valueForKey:@"placeholderLabel"];
            l.font = font;
            break;
        } else if (v.subviews.count) {
            for (UIView *v1 in v.subviews) {
                if ([v1 isKindOfClass:[UITextField class]]) {
                    UITextField *tf = (UITextField *)v1;
                    tf.font = font;
                    UILabel *l = (UILabel *)[tf valueForKey:@"placeholderLabel"];
                    l.font = font;
                    break;
                } else if (v1.subviews.count) {
                    for (UIView *v2 in v1.subviews) {
                        if ([v2 isKindOfClass:[UITextField class]]) {
                            UITextField *tf = (UITextField *)v2;
                            tf.font = font;
                            UILabel *l = (UILabel *)[tf valueForKey:@"placeholderLabel"];
                            l.font = font;
                            break;
                        }
                    }
                }
            }
        }
    }
}

un peu long, mais devrait rendre compte du champ de texte se déplaçant plus profondément dans le futur

0
Halpo

Basé sur Vasily Bodnarchuk , réponses de Aaron Brager , et ullstrm .

Pourquoi un autre?

Personnellement, je n'aime pas avoir as [XXX] et let specific: [Type], mais plutôt de passer le type à des appels de fonction, par exemple.

let scrollViews = view.getNestedSubviews(ofType: UIScrollView.self)
print(scrollViews) // outputs: [UIScrollView]

J'ai également renommé All en Nested car il transmet mieux la nature récursive de la fonction à l'appelant d'API.

Détails

Swift 4.x, Xcode 9.1+

Solution

extension UIView {

    class func getNestedSubviews<T: UIView>(view: UIView) -> [T] {
        return view.subviews.flatMap { subView -> [T] in
            var result = getNestedSubviews(view: subView) as [T]
            if let view = subView as? T {
                result.append(view)
            }
            return result
        }
    }

    func getNestedSubviews<T: UIView>() -> [T] {
        return UIView. getNestedSubviews(view: self) as [T]
    }
}

Usage

let scrollViews = view.getNestedSubviews(ofType: UIScrollView.self)
print(scrollViews) // outputs: [UIScrollView]
0
Jon Willis